diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index ea5e81c3db816..32d73ff32361c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -25,6 +25,7 @@ pub(crate) mod on_const; pub(crate) mod on_move; pub(crate) mod on_unimplemented; pub(crate) mod on_unknown; +pub(crate) mod on_unmatch_args; #[derive(Copy, Clone)] pub(crate) enum Mode { @@ -38,6 +39,8 @@ pub(crate) enum Mode { DiagnosticOnMove, /// `#[diagnostic::on_unknown]` DiagnosticOnUnknown, + /// `#[diagnostic::on_unmatch_args]` + DiagnosticOnUnmatchArgs, } impl Mode { @@ -48,6 +51,7 @@ impl Mode { Self::DiagnosticOnConst => "diagnostic::on_const", Self::DiagnosticOnMove => "diagnostic::on_move", Self::DiagnosticOnUnknown => "diagnostic::on_unknown", + Self::DiagnosticOnUnmatchArgs => "diagnostic::on_unmatch_args", } } @@ -62,6 +66,7 @@ impl Mode { Self::DiagnosticOnConst => DEFAULT, Self::DiagnosticOnMove => DEFAULT, Self::DiagnosticOnUnknown => DEFAULT, + Self::DiagnosticOnUnmatchArgs => DEFAULT, } } @@ -75,6 +80,7 @@ impl Mode { Self::DiagnosticOnConst => DEFAULT, Self::DiagnosticOnMove => DEFAULT, Self::DiagnosticOnUnknown => DEFAULT, + Self::DiagnosticOnUnmatchArgs => DEFAULT, } } } @@ -398,7 +404,9 @@ fn parse_arg( Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) { // Only `#[rustc_on_unimplemented]` can use these (Mode::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext, - (Mode::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This, + (Mode::RustcOnUnimplemented { .. } | Mode::DiagnosticOnUnmatchArgs, sym::This) => { + FormatArg::This + } (Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait, // Any attribute can use these (_, kw::SelfUpper) => FormatArg::SelfUpper, diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatch_args.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatch_args.rs new file mode 100644 index 0000000000000..f541f631bf12f --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unmatch_args.rs @@ -0,0 +1,58 @@ +use rustc_errors::Diagnostic; +use rustc_hir::attrs::diagnostic::Directive; +use rustc_session::lint::builtin::MISPLACED_DIAGNOSTIC_ATTRIBUTES; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; +use crate::errors::DiagnosticOnUnmatchArgsOnlyForMacros; + +#[derive(Default)] +pub(crate) struct OnUnmatchArgsParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl AttributeParser for OnUnmatchArgsParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::diagnostic, sym::on_unmatch_args], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + if !cx.features().diagnostic_on_unmatch_args() { + return; + } + + let span = cx.attr_span; + this.span = Some(span); + + if !matches!(cx.target, Target::MacroDef) { + cx.emit_dyn_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + move |dcx, level| DiagnosticOnUnmatchArgsOnlyForMacros.into_diag(dcx, level), + span, + ); + return; + } + + let mode = Mode::DiagnosticOnUnmatchArgs; + let Some(items) = parse_list(cx, args, mode) else { return }; + + let Some(directive) = parse_directive_items(cx, mode, items.mixed(), true) else { + return; + }; + merge_directives(cx, &mut this.directive, (span, directive)); + }, + )]; + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnUnmatchArgs { + span, + directive: self.directive.map(|d| Box::new(d.1)), + }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index becdaee0f3d3a..5ced5d5f975a0 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -33,6 +33,7 @@ use crate::attributes::diagnostic::on_const::*; use crate::attributes::diagnostic::on_move::*; use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::diagnostic::on_unknown::*; +use crate::attributes::diagnostic::on_unmatch_args::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; use crate::attributes::inline::*; @@ -159,6 +160,7 @@ attribute_parsers!( OnMoveParser, OnUnimplementedParser, OnUnknownParser, + OnUnmatchArgsParser, RustcAlignParser, RustcAlignStaticParser, RustcCguTestAttributeParser, diff --git a/compiler/rustc_attr_parsing/src/errors.rs b/compiler/rustc_attr_parsing/src/errors.rs index 96a4c473c3ae2..d3e6845799e7f 100644 --- a/compiler/rustc_attr_parsing/src/errors.rs +++ b/compiler/rustc_attr_parsing/src/errors.rs @@ -315,6 +315,10 @@ pub(crate) struct DiagnosticOnUnknownOnlyForImports { pub target_span: Span, } +#[derive(Diagnostic)] +#[diag("`#[diagnostic::on_unmatch_args]` can only be applied to macro definitions")] +pub(crate) struct DiagnosticOnUnmatchArgsOnlyForMacros; + #[derive(Diagnostic)] #[diag("`#[diagnostic::do_not_recommend]` can only be placed on trait implementations")] pub(crate) struct IncorrectDoNotRecommendLocation { diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 19735a29dfb2b..be1733086c837 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_target::spec::{Arch, Env, LlvmAbi, RustcAbi}; use crate::builder::Builder; -use crate::llvm::{Type, Value}; +use crate::llvm::Value; use crate::type_of::LayoutLlvmExt; fn round_up_to_alignment<'ll>( @@ -27,13 +27,14 @@ fn round_pointer_up_to_alignment<'ll>( bx: &mut Builder<'_, 'll, '_>, addr: &'ll Value, align: Align, - ptr_ty: &'ll Type, ) -> &'ll Value { let ptr = bx.inbounds_ptradd(addr, bx.const_i32(align.bytes() as i32 - 1)); + let pointer_width = bx.tcx().sess.target.pointer_width; + let mask = align.bytes().wrapping_neg() & (u64::MAX >> (64 - pointer_width)); bx.call_intrinsic( "llvm.ptrmask", - &[ptr_ty, bx.type_i32()], - &[ptr, bx.const_int(bx.isize_ty, -(align.bytes() as isize) as i64)], + &[bx.type_ptr(), bx.type_isize()], + &[ptr, bx.const_usize(mask)], ) } @@ -53,7 +54,7 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>( let ptr = bx.load(va_list_ty, va_list_addr, ptr_align_abi); let (addr, addr_align) = if allow_higher_align && align > slot_size { - (round_pointer_up_to_alignment(bx, ptr, align, bx.type_ptr()), align) + (round_pointer_up_to_alignment(bx, ptr, align), align) } else { (ptr, slot_size) }; @@ -69,7 +70,8 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>( { let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32); let adjusted = bx.inbounds_ptradd(addr, adjusted_size); - (adjusted, addr_align) + // We're in the middle of a slot now, so use the type's alignment, not the slot's. + (adjusted, align) } else { (addr, addr_align) } @@ -357,12 +359,8 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( // Round up address of argument to alignment if layout.layout.align.abi > overflow_area_align { - overflow_area = round_pointer_up_to_alignment( - bx, - overflow_area, - layout.layout.align.abi, - bx.type_ptr(), - ); + overflow_area = + round_pointer_up_to_alignment(bx, overflow_area, layout.layout.align.abi); } let mem_addr = overflow_area; @@ -827,7 +825,7 @@ fn emit_hexagon_va_arg_musl<'ll, 'tcx>( } else { Align::from_bytes(4).unwrap() }; - let aligned_current = round_pointer_up_to_alignment(bx, current_ptr, arg_align, bx.type_ptr()); + let aligned_current = round_pointer_up_to_alignment(bx, current_ptr, arg_align); // Calculate next pointer position (following LLVM's logic) // Arguments <= 32 bits take 4 bytes, > 32 bits take 8 bytes @@ -849,8 +847,7 @@ fn emit_hexagon_va_arg_musl<'ll, 'tcx>( bx.switch_to_block(from_overflow); // Align overflow pointer using the same alignment rules - let aligned_overflow = - round_pointer_up_to_alignment(bx, overflow_ptr, arg_align, bx.type_ptr()); + let aligned_overflow = round_pointer_up_to_alignment(bx, overflow_ptr, arg_align); let overflow_value_addr = aligned_overflow; // Update overflow pointer - use the same size calculation @@ -890,7 +887,7 @@ fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>( let aligned_ptr = if ty_align.bytes() > 4 { // Ensure alignment is a power of 2 debug_assert!(ty_align.bytes().is_power_of_two(), "Alignment is not power of 2!"); - round_pointer_up_to_alignment(bx, current_ptr, ty_align, bx.type_ptr()) + round_pointer_up_to_alignment(bx, current_ptr, ty_align) } else { current_ptr }; diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index b8040c3d9c1a8..4e7e51c0a43cb 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage}; +use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; use rustc_macros::Subdiagnostic; use rustc_parse::parser::{Parser, Recovery, token_descr}; use rustc_session::parse::ParseSess; @@ -32,6 +33,7 @@ pub(super) fn failed_to_match_macro( args: FailedMacro<'_>, body: &TokenStream, rules: &[MacroRule], + on_unmatch_args: Option<&Directive>, ) -> (Span, ErrorGuaranteed) { debug!("failed to match macro"); let def_head_span = if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) { @@ -72,9 +74,30 @@ pub(super) fn failed_to_match_macro( }; let span = token.span.substitute_dummy(sp); + let CustomDiagnostic { + message: custom_message, label: custom_label, notes: custom_notes, .. + } = { + let macro_name = name.to_string(); + on_unmatch_args + .map(|directive| { + directive.eval( + None, + &FormatArgs { + this: macro_name.clone(), + this_sugared: macro_name, + item_context: "macro invocation", + generic_args: Vec::new(), + }, + ) + }) + .unwrap_or_default() + }; - let mut err = psess.dcx().struct_span_err(span, parse_failure_msg(&token, None)); - err.span_label(span, label); + let mut err = match custom_message { + Some(message) => psess.dcx().struct_span_err(span, message), + None => psess.dcx().struct_span_err(span, parse_failure_msg(&token, None)), + }; + err.span_label(span, custom_label.unwrap_or_else(|| label.to_string())); if !def_head_span.is_dummy() { err.span_label(def_head_span, "when calling this macro"); } @@ -86,6 +109,9 @@ pub(super) fn failed_to_match_macro( } else { err.note(format!("while trying to match {remaining_matcher}")); } + for note in custom_notes { + err.note(note); + } if let MatcherLoc::Token { token: expected_token } = &remaining_matcher && (matches!(expected_token.kind, token::OpenInvisible(_)) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index fd5dac3cd9263..eed13a13fa911 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -14,6 +14,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_feature::Features; use rustc_hir as hir; +use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::def::MacroKinds; use rustc_hir::find_attr; use rustc_lint_defs::builtin::{ @@ -164,6 +165,7 @@ pub struct MacroRulesMacroExpander { node_id: NodeId, name: Ident, span: Span, + on_unmatch_args: Option, transparency: Transparency, kinds: MacroKinds, rules: Vec, @@ -194,7 +196,8 @@ impl MacroRulesMacroExpander { ) -> Result { // This is similar to `expand_macro`, but they have very different signatures, and will // diverge further once derives support arguments. - let Self { name, ref rules, node_id, .. } = *self; + let name = self.name; + let rules = &self.rules; let psess = &cx.sess.psess; if cx.trace_macros() { @@ -220,8 +223,8 @@ impl MacroRulesMacroExpander { trace_macros_note(&mut cx.expansions, sp, msg); } - if is_defined_in_current_crate(node_id) { - cx.resolver.record_macro_rule_usage(node_id, rule_index); + if is_defined_in_current_crate(self.node_id) { + cx.resolver.record_macro_rule_usage(self.node_id, rule_index); } Ok(tts) @@ -236,6 +239,7 @@ impl MacroRulesMacroExpander { FailedMacro::Derive, body, rules, + self.on_unmatch_args.as_ref(), ); cx.macro_error_and_trace_macros_diag(); Err(guar) @@ -260,6 +264,7 @@ impl TTMacroExpander for MacroRulesMacroExpander { self.transparency, input, &self.rules, + self.on_unmatch_args.as_ref(), )) } } @@ -294,6 +299,7 @@ impl AttrProcMacro for MacroRulesMacroExpander { args, body, &self.rules, + self.on_unmatch_args.as_ref(), ) } } @@ -355,7 +361,7 @@ impl<'matcher> Tracker<'matcher> for NoopTracker { } /// Expands the rules based macro defined by `rules` for a given input `arg`. -#[instrument(skip(cx, transparency, arg, rules))] +#[instrument(skip(cx, transparency, arg, rules, on_unmatch_args))] fn expand_macro<'cx, 'a: 'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, @@ -365,6 +371,7 @@ fn expand_macro<'cx, 'a: 'cx>( transparency: Transparency, arg: TokenStream, rules: &'a [MacroRule], + on_unmatch_args: Option<&Directive>, ) -> Box { let psess = &cx.sess.psess; @@ -423,6 +430,7 @@ fn expand_macro<'cx, 'a: 'cx>( FailedMacro::Func, &arg, rules, + on_unmatch_args, ); cx.macro_error_and_trace_macros_diag(); DummyResult::any(span, guar) @@ -431,7 +439,7 @@ fn expand_macro<'cx, 'a: 'cx>( } /// Expands the rules based macro defined by `rules` for a given attribute `args` and `body`. -#[instrument(skip(cx, transparency, args, body, rules))] +#[instrument(skip(cx, transparency, args, body, rules, on_unmatch_args))] fn expand_macro_attr( cx: &mut ExtCtxt<'_>, sp: Span, @@ -443,6 +451,7 @@ fn expand_macro_attr( args: TokenStream, body: TokenStream, rules: &[MacroRule], + on_unmatch_args: Option<&Directive>, ) -> Result { let psess = &cx.sess.psess; // Macros defined in the current crate have a real node id, @@ -507,6 +516,7 @@ fn expand_macro_attr( FailedMacro::Attr(&args), &body, rules, + on_unmatch_args, ); cx.trace_macros_diag(); Err(guar) @@ -849,7 +859,22 @@ pub fn compile_declarative_macro( // Return the number of rules for unused rule linting, if this is a local macro. let nrules = if is_defined_in_current_crate(node_id) { rules.len() } else { 0 }; - let exp = MacroRulesMacroExpander { name: ident, kinds, span, node_id, transparency, rules }; + let on_unmatch_args = find_attr!( + attrs, + OnUnmatchArgs { directive, .. } => directive.clone() + ) + .flatten() + .map(|directive| *directive); + + let exp = MacroRulesMacroExpander { + name: ident, + kinds, + span, + node_id, + on_unmatch_args, + transparency, + rules, + }; (mk_syn_ext(SyntaxExtensionKind::MacroRules(Arc::new(exp))), nrules) } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 82402b3c7e31c..c175c3adc2f02 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -480,6 +480,8 @@ declare_features! ( (unstable, diagnostic_on_move, "1.96.0", Some(154181)), /// Allows giving unresolved imports a custom diagnostic message (unstable, diagnostic_on_unknown, "1.96.0", Some(152900)), + /// Allows macros to customize macro argument matcher diagnostics. + (unstable, diagnostic_on_unmatch_args, "CURRENT_RUSTC_VERSION", Some(155642)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 05f398058ecaa..78ce2f019a390 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1208,6 +1208,13 @@ pub enum AttributeKind { directive: Option>, }, + /// Represents `#[diagnostic::on_unmatch_args]`. + OnUnmatchArgs { + span: Span, + /// None if the directive was malformed in some way. + directive: Option>, + }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index ad4d0728888bf..41e466a9d16b7 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -80,6 +80,7 @@ impl AttributeKind { OnMove { .. } => Yes, OnUnimplemented { .. } => Yes, OnUnknown { .. } => Yes, + OnUnmatchArgs { .. } => Yes, Optimize(..) => No, PanicRuntime => No, PatchableFunctionEntry { .. } => Yes, diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 8b08c7b68b784..1e9986fa761c4 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -34,19 +34,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs: &'tcx Expr<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let (lhs_ty, rhs_ty, return_ty) = + let (lhs_ty, rhs_ty, _return_ty) = self.check_overloaded_binop(expr, lhs, rhs, Op::AssignOp(op), expected); let category = BinOpCategory::from(op.node); - let ty = if !lhs_ty.is_ty_var() - && !rhs_ty.is_ty_var() - && is_builtin_binop(lhs_ty, rhs_ty, category) + if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, category) { self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category); - self.tcx.types.unit - } else { - return_ty - }; + } self.check_lhs_assignable(lhs, E0067, op.span, |err| { if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { @@ -86,7 +81,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }); - ty + self.tcx.types.unit } /// Checks a potentially overloaded binary operator. @@ -1311,21 +1306,16 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> { } /// Returns `true` if this is a built-in arithmetic operation (e.g., -/// u32 + u32, i16x4 == i16x4) and false if these types would have to be -/// overloaded to be legal. There are two reasons that we distinguish +/// u32 + u32) and false if these types would have to be +/// overloaded to be legal. The reason that we distinguish /// builtin operations from overloaded ones (vs trying to drive /// everything uniformly through the trait system and intrinsics or -/// something like that): -/// -/// 1. Builtin operations can trivially be evaluated in constants. -/// 2. For comparison operators applied to SIMD types the result is -/// not of type `bool`. For example, `i16x4 == i16x4` yields a -/// type like `i16x4`. This means that the overloaded trait -/// `PartialEq` is not applicable. +/// something like that) is that builtin operations can trivially +/// be evaluated in constants on stable, but the traits and their +/// impls for these primitive types. /// -/// Reason #2 is the killer. I tried for a while to always use -/// overloaded logic and just check the types in constants/codegen after -/// the fact, and it worked fine, except for SIMD types. -nmatsakis +/// FIXME(const_trait_impls): once the traits and their impls are const stable +/// remove this function and the builtin-specific checks. fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool { // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. // (See https://github.com/rust-lang/rust/issues/57447.) diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 60bfdd689609d..92c4748483278 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -52,7 +52,6 @@ impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> { lints::MalFormedDiagnosticAttributeLint { attribute, options, span } .into_diag(dcx, level) } - AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning { FormatWarning::PositionalArgument { .. } => { lints::DisallowedPositionalArgument.into_diag(dcx, level) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 280b3b12e5c85..181dbfa126d73 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -221,7 +221,7 @@ impl<'a> LazyDecoder for BlobDecodeContext<'a> { /// This is the decode context used when crate metadata was already read. /// Decoding of some types, like `Span` require some information to already been read. -/// Can be constructed from a [`TyCtxt`] and [`CrateMetadataRef`] (see the [`Metadata`] trait) +/// Can be constructed from a [`TyCtxt`] and [`CrateMetadataRef`] (see the [`MetaDecoder`] trait) pub(super) struct MetadataDecodeContext<'a, 'tcx> { blob_decoder: BlobDecodeContext<'a>, cdata: CrateMetadataRef<'a>, @@ -255,19 +255,24 @@ impl<'a, 'tcx> Deref for MetadataDecodeContext<'a, 'tcx> { } } -pub(super) trait Metadata<'a>: Copy { +pub(super) trait MetaBlob<'a>: Copy { + fn blob(&self) -> &'a MetadataBlob; +} + +pub(super) trait MetaDecoder: Copy { type Context: BlobDecoder + LazyDecoder; - fn blob(self) -> &'a MetadataBlob; fn decoder(self, pos: usize) -> Self::Context; } -impl<'a> Metadata<'a> for &'a MetadataBlob { - type Context = BlobDecodeContext<'a>; - - fn blob(self) -> &'a MetadataBlob { +impl<'a> MetaBlob<'a> for &'a MetadataBlob { + fn blob(&self) -> &'a MetadataBlob { self } +} + +impl<'a> MetaDecoder for &'a MetadataBlob { + type Context = BlobDecodeContext<'a>; fn decoder(self, pos: usize) -> Self::Context { BlobDecodeContext { @@ -285,16 +290,18 @@ impl<'a> Metadata<'a> for &'a MetadataBlob { } } -impl<'a, 'tcx> Metadata<'a> for (CrateMetadataRef<'a>, TyCtxt<'tcx>) { - type Context = MetadataDecodeContext<'a, 'tcx>; - - fn blob(self) -> &'a MetadataBlob { - &self.0.cdata.blob +impl<'a> MetaBlob<'a> for &'a CrateMetadata { + fn blob(&self) -> &'a MetadataBlob { + &self.blob } +} + +impl<'a, 'tcx> MetaDecoder for (CrateMetadataRef<'a>, TyCtxt<'tcx>) { + type Context = MetadataDecodeContext<'a, 'tcx>; fn decoder(self, pos: usize) -> MetadataDecodeContext<'a, 'tcx> { MetadataDecodeContext { - blob_decoder: self.blob().decoder(pos), + blob_decoder: self.0.cdata.blob().decoder(pos), cdata: self.0, tcx: self.1, alloc_decoding_session: self.0.cdata.alloc_decoding_state.new_decoding_session(), @@ -304,7 +311,7 @@ impl<'a, 'tcx> Metadata<'a> for (CrateMetadataRef<'a>, TyCtxt<'tcx>) { impl LazyValue { #[inline] - fn decode<'a, 'tcx, M: Metadata<'a>>(self, metadata: M) -> T::Value<'tcx> + fn decode<'tcx, M: MetaDecoder>(self, metadata: M) -> T::Value<'tcx> where T::Value<'tcx>: Decodable, { @@ -344,10 +351,7 @@ unsafe impl> TrustedLen for DecodeIterator {} impl LazyArray { #[inline] - fn decode<'a, 'tcx, M: Metadata<'a>>( - self, - metadata: M, - ) -> DecodeIterator, M::Context> + fn decode<'tcx, M: MetaDecoder>(self, metadata: M) -> DecodeIterator, M::Context> where T::Value<'tcx>: Decodable, { @@ -468,7 +472,7 @@ impl<'a, 'tcx> SpanDecoder for MetadataDecodeContext<'a, 'tcx> { cdata .root .syntax_contexts - .get((cdata, tcx), id) + .get(cdata.cdata, id) .unwrap_or_else(|| panic!("Missing SyntaxContext {id:?} for crate {cname:?}")) .decode((cdata, tcx)) }) @@ -494,13 +498,13 @@ impl<'a, 'tcx> SpanDecoder for MetadataDecodeContext<'a, 'tcx> { let expn_data = crate_data .root .expn_data - .get((crate_data, tcx), index) + .get(crate_data.cdata, index) .unwrap() .decode((crate_data, tcx)); let expn_hash = crate_data .root .expn_hashes - .get((crate_data, tcx), index) + .get(crate_data.cdata, index) .unwrap() .decode((crate_data, tcx)); (expn_data, expn_hash) @@ -1017,7 +1021,7 @@ impl<'a> CrateMetadataRef<'a> { .root .tables .def_ident_span - .get((self, tcx), item_index) + .get(self.cdata, item_index) .unwrap_or_else(|| self.missing("def_ident_span", item_index)) .decode((self, tcx)); Some(Ident::new(name, span)) @@ -1032,11 +1036,11 @@ impl<'a> CrateMetadataRef<'a> { if cnum == LOCAL_CRATE { self.cnum } else { self.cnum_map[cnum] } } - fn def_kind(self, tcx: TyCtxt<'_>, item_id: DefIndex) -> DefKind { + fn def_kind(self, item_id: DefIndex) -> DefKind { self.root .tables .def_kind - .get((self, tcx), item_id) + .get(self.cdata, item_id) .unwrap_or_else(|| self.missing("def_kind", item_id)) } @@ -1044,7 +1048,7 @@ impl<'a> CrateMetadataRef<'a> { self.root .tables .def_span - .get((self, tcx), index) + .get(self.cdata, index) .unwrap_or_else(|| self.missing("def_span", index)) .decode((self, tcx)) } @@ -1097,7 +1101,7 @@ impl<'a> CrateMetadataRef<'a> { }; let data = - self.root.tables.variant_data.get((self, tcx), index).unwrap().decode((self, tcx)); + self.root.tables.variant_data.get(self.cdata, index).unwrap().decode((self, tcx)); let variant_did = if adt_kind == ty::AdtKind::Enum { Some(self.local_def_id(index)) } else { None }; @@ -1115,7 +1119,7 @@ impl<'a> CrateMetadataRef<'a> { did, name: self.item_name(did.index), vis: self.get_visibility(tcx, did.index), - safety: self.get_safety(tcx, did.index), + safety: self.get_safety(did.index), value: self.get_default_field(tcx, did.index), }) .collect(), @@ -1127,7 +1131,7 @@ impl<'a> CrateMetadataRef<'a> { } fn get_adt_def<'tcx>(self, tcx: TyCtxt<'tcx>, item_id: DefIndex) -> ty::AdtDef<'tcx> { - let kind = self.def_kind(tcx, item_id); + let kind = self.def_kind(item_id); let did = self.local_def_id(item_id); let adt_kind = match kind { @@ -1137,17 +1141,17 @@ impl<'a> CrateMetadataRef<'a> { _ => bug!("get_adt_def called on a non-ADT {:?}", did), }; let repr = - self.root.tables.repr_options.get((self, tcx), item_id).unwrap().decode((self, tcx)); + self.root.tables.repr_options.get(self.cdata, item_id).unwrap().decode((self, tcx)); let mut variants: Vec<_> = if let ty::AdtKind::Enum = adt_kind { self.root .tables .module_children_non_reexports - .get((self, tcx), item_id) + .get(self.cdata, item_id) .expect("variants are not encoded for an enum") .decode((self, tcx)) .filter_map(|index| { - let kind = self.def_kind(tcx, index); + let kind = self.def_kind(index); match kind { DefKind::Ctor(..) => None, _ => Some(self.get_variant(tcx, kind, index, did)), @@ -1172,25 +1176,25 @@ impl<'a> CrateMetadataRef<'a> { self.root .tables .visibility - .get((self, tcx), id) + .get(self.cdata, id) .unwrap_or_else(|| self.missing("visibility", id)) .decode((self, tcx)) .map_id(|index| self.local_def_id(index)) } - fn get_safety(self, tcx: TyCtxt<'_>, id: DefIndex) -> Safety { - self.root.tables.safety.get((self, tcx), id) + fn get_safety(self, id: DefIndex) -> Safety { + self.root.tables.safety.get(self.cdata, id) } fn get_default_field(self, tcx: TyCtxt<'_>, id: DefIndex) -> Option { - self.root.tables.default_fields.get((self, tcx), id).map(|d| d.decode((self, tcx))) + self.root.tables.default_fields.get(self.cdata, id).map(|d| d.decode((self, tcx))) } fn get_expn_that_defined(self, tcx: TyCtxt<'_>, id: DefIndex) -> ExpnId { self.root .tables .expn_that_defined - .get((self, tcx), id) + .get(self.cdata, id) .unwrap_or_else(|| self.missing("expn_that_defined", id)) .decode((self, tcx)) } @@ -1259,7 +1263,7 @@ impl<'a> CrateMetadataRef<'a> { fn get_mod_child(self, tcx: TyCtxt<'_>, id: DefIndex) -> ModChild { let ident = self.item_ident(tcx, id); - let res = Res::Def(self.def_kind(tcx, id), self.local_def_id(id)); + let res = Res::Def(self.def_kind(id), self.local_def_id(id)); let vis = self.get_visibility(tcx, id); ModChild { ident, res, vis, reexport_chain: Default::default() } @@ -1282,12 +1286,12 @@ impl<'a> CrateMetadataRef<'a> { } else { // Iterate over all children. let non_reexports = - self.root.tables.module_children_non_reexports.get((self, tcx), id); + self.root.tables.module_children_non_reexports.get(self.cdata, id); for child_index in non_reexports.unwrap().decode((self, tcx)) { yield self.get_mod_child(tcx, child_index); } - let reexports = self.root.tables.module_children_reexports.get((self, tcx), id); + let reexports = self.root.tables.module_children_reexports.get(self.cdata, id); if !reexports.is_default() { for reexport in reexports.decode((self, tcx)) { yield reexport; @@ -1303,7 +1307,7 @@ impl<'a> CrateMetadataRef<'a> { id: DefIndex, ) -> impl Iterator { gen move { - let children = self.root.tables.ambig_module_children.get((self, tcx), id); + let children = self.root.tables.ambig_module_children.get(self.cdata, id); if !children.is_default() { for child in children.decode((self, tcx)) { yield child; @@ -1312,15 +1316,15 @@ impl<'a> CrateMetadataRef<'a> { } } - fn is_item_mir_available(self, tcx: TyCtxt<'_>, id: DefIndex) -> bool { - self.root.tables.optimized_mir.get((self, tcx), id).is_some() + fn is_item_mir_available(self, id: DefIndex) -> bool { + self.root.tables.optimized_mir.get(self.cdata, id).is_some() } fn get_fn_has_self_parameter(self, tcx: TyCtxt<'_>, id: DefIndex) -> bool { self.root .tables .fn_arg_idents - .get((self, tcx), id) + .get(self.cdata, id) .expect("argument names not encoded for a function") .decode((self, tcx)) .nth(0) @@ -1335,14 +1339,14 @@ impl<'a> CrateMetadataRef<'a> { self.root .tables .associated_item_or_field_def_ids - .get((self, tcx), id) + .get(self.cdata, id) .unwrap_or_else(|| self.missing("associated_item_or_field_def_ids", id)) .decode((self, tcx)) .map(move |child_index| self.local_def_id(child_index)) } fn get_associated_item(self, tcx: TyCtxt<'_>, id: DefIndex) -> ty::AssocItem { - let kind = match self.def_kind(tcx, id) { + let kind = match self.def_kind(id) { DefKind::AssocConst { is_type_const } => { ty::AssocKind::Const { name: self.item_name(id), is_type_const } } @@ -1352,7 +1356,7 @@ impl<'a> CrateMetadataRef<'a> { }, DefKind::AssocTy => { let data = if let Some(rpitit_info) = - self.root.tables.opt_rpitit_info.get((self, tcx), id) + self.root.tables.opt_rpitit_info.get(self.cdata, id) { ty::AssocTypeData::Rpitit(rpitit_info.decode((self, tcx))) } else { @@ -1363,19 +1367,19 @@ impl<'a> CrateMetadataRef<'a> { _ => bug!("cannot get associated-item of `{:?}`", self.def_key(id)), }; let container = - self.root.tables.assoc_container.get((self, tcx), id).unwrap().decode((self, tcx)); + self.root.tables.assoc_container.get(self.cdata, id).unwrap().decode((self, tcx)); ty::AssocItem { kind, def_id: self.local_def_id(id), container } } fn get_ctor(self, tcx: TyCtxt<'_>, node_id: DefIndex) -> Option<(CtorKind, DefId)> { - match self.def_kind(tcx, node_id) { + match self.def_kind(node_id) { DefKind::Struct | DefKind::Variant => { let vdata = self .root .tables .variant_data - .get((self, tcx), node_id) + .get(self.cdata, node_id) .unwrap() .decode((self, tcx)); vdata.ctor.map(|(kind, index)| (kind, self.local_def_id(index))) @@ -1388,7 +1392,7 @@ impl<'a> CrateMetadataRef<'a> { self.root .tables .attributes - .get((self, tcx), id) + .get(self.cdata, id) .unwrap_or_else(|| { // Structure and variant constructors don't have any attributes encoded for them, // but we assume that someone passing a constructor ID actually wants to look at @@ -1399,7 +1403,7 @@ impl<'a> CrateMetadataRef<'a> { self.root .tables .attributes - .get((self, tcx), parent_id) + .get(self.cdata, parent_id) .expect("no encoded attributes for a structure or variant") }) .decode((self, tcx)) @@ -1414,7 +1418,7 @@ impl<'a> CrateMetadataRef<'a> { self.root .tables .inherent_impls - .get((self, tcx), id) + .get(self.cdata, id) .decode((self, tcx)) .map(|index| self.local_def_id(index)), ) @@ -1475,7 +1479,7 @@ impl<'a> CrateMetadataRef<'a> { self.root .tables .proc_macro_quoted_spans - .get((self, tcx), index) + .get(self.cdata, index) .unwrap_or_else(|| panic!("Missing proc macro quoted span: {index:?}")) .decode((self, tcx)) } @@ -1538,14 +1542,14 @@ impl<'a> CrateMetadataRef<'a> { } fn get_macro(self, tcx: TyCtxt<'_>, id: DefIndex) -> ast::MacroDef { - match self.def_kind(tcx, id) { + match self.def_kind(id) { DefKind::Macro(_) => { - let macro_rules = self.root.tables.is_macro_rules.get((self, tcx), id); + let macro_rules = self.root.tables.is_macro_rules.get(self.cdata, id); let body = self .root .tables .macro_definition - .get((self, tcx), id) + .get(self.cdata, id) .unwrap() .decode((self, tcx)); ast::MacroDef { macro_rules, body: Box::new(body), eii_declaration: None } @@ -1586,11 +1590,8 @@ impl<'a> CrateMetadataRef<'a> { fn expn_hash_to_expn_id(self, tcx: TyCtxt<'_>, index_guess: u32, hash: ExpnHash) -> ExpnId { let index_guess = ExpnIndex::from_u32(index_guess); - let old_hash = self - .root - .expn_hashes - .get((self, tcx), index_guess) - .map(|lazy| lazy.decode((self, tcx))); + let old_hash = + self.root.expn_hashes.get(self.cdata, index_guess).map(|lazy| lazy.decode((self, tcx))); let index = if old_hash == Some(hash) { // Fast path: the expn and its index is unchanged from the @@ -1607,7 +1608,7 @@ impl<'a> CrateMetadataRef<'a> { UnhashMap::with_capacity_and_hasher(end_id as usize, Default::default()); for i in 0..end_id { let i = ExpnIndex::from_u32(i); - if let Some(hash) = self.root.expn_hashes.get((self, tcx), i) { + if let Some(hash) = self.root.expn_hashes.get(self.cdata, i) { map.insert(hash.decode((self, tcx)), i); } } @@ -1616,7 +1617,7 @@ impl<'a> CrateMetadataRef<'a> { map[&hash] }; - let data = self.root.expn_data.get((self, tcx), index).unwrap().decode((self, tcx)); + let data = self.root.expn_data.get(self.cdata, index).unwrap().decode((self, tcx)); rustc_span::hygiene::register_expn_id(self.cnum, index, data, hash) } @@ -1753,7 +1754,7 @@ impl<'a> CrateMetadataRef<'a> { let source_file_to_import = self .root .source_map - .get((self, tcx), source_file_index) + .get(self.cdata, source_file_index) .expect("missing source file") .decode((self, tcx)); @@ -1854,19 +1855,19 @@ impl<'a> CrateMetadataRef<'a> { .clone() } - fn get_attr_flags(self, tcx: TyCtxt<'_>, index: DefIndex) -> AttrFlags { - self.root.tables.attr_flags.get((self, tcx), index) + fn get_attr_flags(self, index: DefIndex) -> AttrFlags { + self.root.tables.attr_flags.get(self.cdata, index) } fn get_intrinsic(self, tcx: TyCtxt<'_>, index: DefIndex) -> Option { - self.root.tables.intrinsic.get((self, tcx), index).map(|d| d.decode((self, tcx))) + self.root.tables.intrinsic.get(self.cdata, index).map(|d| d.decode((self, tcx))) } fn get_doc_link_resolutions(self, tcx: TyCtxt<'_>, index: DefIndex) -> DocLinkResMap { self.root .tables .doc_link_resolutions - .get((self, tcx), index) + .get(self.cdata, index) .expect("no resolutions for a doc link") .decode((self, tcx)) } @@ -1879,7 +1880,7 @@ impl<'a> CrateMetadataRef<'a> { self.root .tables .doc_link_traits_in_scope - .get((self, tcx), index) + .get(self.cdata, index) .expect("no traits in scope for a doc link") .decode((self, tcx)) } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index f9f746188f239..c82bed7761aa5 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -100,7 +100,7 @@ macro_rules! provide_one { .root .tables .$name - .get(($cdata, $tcx), $def_id.index) + .get($cdata.cdata, $def_id.index) .map(|lazy| lazy.decode(($cdata, $tcx))) .process_decoded($tcx, || panic!("{:?} does not have a {:?}", $def_id, stringify!($name))) } @@ -109,7 +109,7 @@ macro_rules! provide_one { ($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => { table_defaulted_array }) => { provide_one! { $tcx, $def_id, $other, $cdata, $name => { - let lazy = $cdata.root.tables.$name.get(($cdata, $tcx), $def_id.index); + let lazy = $cdata.root.tables.$name.get($cdata.cdata, $def_id.index); let value = if lazy.is_default() { &[] as &[_] } else { @@ -127,7 +127,7 @@ macro_rules! provide_one { .root .tables .$name - .get(($cdata, $tcx), $def_id.index) + .get($cdata.cdata, $def_id.index) .process_decoded($tcx, || panic!("{:?} does not have a {:?}", $def_id, stringify!($name))) } } @@ -253,7 +253,7 @@ provide! { tcx, def_id, other, cdata, lookup_default_body_stability => { table } lookup_deprecation_entry => { table } params_in_repr => { table } - def_kind => { cdata.def_kind(tcx, def_id.index) } + def_kind => { cdata.def_kind(def_id.index) } impl_parent => { table } defaultness => { table_direct } constness => { table_direct } @@ -264,7 +264,7 @@ provide! { tcx, def_id, other, cdata, .root .tables .coerce_unsized_info - .get((cdata, tcx), def_id.index) + .get(cdata.cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) } mir_const_qualif => { table } @@ -280,7 +280,7 @@ provide! { tcx, def_id, other, cdata, .root .tables .eval_static_initializer - .get((cdata, tcx), def_id.index) + .get(cdata.cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) .unwrap_or_else(|| panic!("{def_id:?} does not have eval_static_initializer"))) } @@ -293,7 +293,7 @@ provide! { tcx, def_id, other, cdata, .root .tables .deduced_param_attrs - .get((cdata, tcx), def_id.index) + .get(cdata.cdata, def_id.index) .map(|lazy| { &*tcx.arena.alloc_from_iter(lazy.decode((cdata, tcx))) }) @@ -306,7 +306,7 @@ provide! { tcx, def_id, other, cdata, .root .tables .trait_impl_trait_tys - .get((cdata, tcx), def_id.index) + .get(cdata.cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) } @@ -323,7 +323,7 @@ provide! { tcx, def_id, other, cdata, associated_item => { cdata.get_associated_item(tcx, def_id.index) } inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } attrs_for_def => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(tcx, def_id.index)) } - is_mir_available => { cdata.is_item_mir_available(tcx, def_id.index) } + is_mir_available => { cdata.is_item_mir_available(def_id.index) } cross_crate_inlinable => { table_direct } dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) } @@ -411,7 +411,7 @@ provide! { tcx, def_id, other, cdata, crate_extern_paths => { cdata.source().paths().cloned().collect() } expn_that_defined => { cdata.get_expn_that_defined(tcx, def_id.index) } default_field => { cdata.get_default_field(tcx, def_id.index) } - is_doc_hidden => { cdata.get_attr_flags(tcx,def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) } + is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) } doc_link_resolutions => { tcx.arena.alloc(cdata.get_doc_link_resolutions(tcx, def_id.index)) } doc_link_traits_in_scope => { tcx.arena.alloc_from_iter(cdata.get_doc_link_traits_in_scope(tcx, def_id.index)) @@ -613,8 +613,8 @@ impl CStore { self.get_crate_data(def_id.krate).get_span(tcx, def_id.index) } - pub fn def_kind_untracked(&self, tcx: TyCtxt<'_>, def: DefId) -> DefKind { - self.get_crate_data(def.krate).def_kind(tcx, def.index) + pub fn def_kind_untracked(&self, def: DefId) -> DefKind { + self.get_crate_data(def.krate).def_kind(def.index) } pub fn expn_that_defined_untracked(&self, tcx: TyCtxt<'_>, def_id: DefId) -> ExpnId { diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 3b0570dd373a8..26c5908563777 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -1,7 +1,7 @@ use rustc_hir::def::CtorOf; use rustc_index::Idx; -use crate::rmeta::decoder::Metadata; +use crate::rmeta::decoder::MetaBlob; use crate::rmeta::*; pub(super) trait IsDefault: Default { @@ -515,7 +515,7 @@ where for<'tcx> T::Value<'tcx>: FixedSizeEncoding, { /// Given the metadata, extract out the value at a particular index (if any). - pub(super) fn get<'a, 'tcx, M: Metadata<'a>>(&self, metadata: M, i: I) -> T::Value<'tcx> { + pub(super) fn get<'a, 'tcx, M: MetaBlob<'a>>(&self, metadata: M, i: I) -> T::Value<'tcx> { // Access past the end of the table returns a Default if i.index() >= self.len { return Default::default(); diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index be29e053a17c0..87d5ae5aaa783 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -5,12 +5,11 @@ use std::sync::Arc; use std::sync::atomic::{AtomicU32, Ordering}; use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::outline; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::profiling::QueryInvocationId; use rustc_data_structures::sharded::{self, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::{AtomicU64, Lock, is_dyn_thread_safe}; +use rustc_data_structures::sync::{AtomicU64, Lock}; use rustc_data_structures::unord::UnordMap; use rustc_errors::DiagInner; use rustc_index::IndexVec; @@ -635,11 +634,6 @@ impl DepGraphData { if !ok { panic!("{}", msg()) } - } else if let Some(nodes_in_current_session) = &self.current.nodes_in_current_session { - outline(|| { - let seen = nodes_in_current_session.lock().contains_key(dep_node); - assert!(!seen, "{}", msg()); - }); } } @@ -775,7 +769,8 @@ impl DepGraphData { is_green, ); - self.current.record_node(dep_node_index, key, value_fingerprint); + #[cfg(debug_assertions)] + self.current.record_edge(dep_node_index, key, value_fingerprint); dep_node_index } else { @@ -787,8 +782,6 @@ impl DepGraphData { &self, prev_index: SerializedDepNodeIndex, ) -> Option { - self.current.debug_assert_not_in_new_nodes(&self.previous, prev_index); - let dep_node_index = self.current.encoder.send_promoted(prev_index, &self.colors); #[cfg(debug_assertions)] @@ -1113,13 +1106,6 @@ pub(super) struct CurrentDepGraph { #[cfg(debug_assertions)] forbidden_edge: Option, - /// Used to verify the absence of hash collisions among DepNodes. - /// This field is only `Some` if the `-Z incremental_verify_ich` option is present - /// or if `debug_assertions` are enabled. - /// - /// The map contains all DepNodes that have been allocated in the current session so far. - nodes_in_current_session: Option>>, - /// Anonymous `DepNode`s are nodes whose IDs we compute from the list of /// their edges. This has the beneficial side-effect that multiple anonymous /// nodes can be coalesced into one without changing the semantics of the @@ -1160,9 +1146,6 @@ impl CurrentDepGraph { let new_node_count_estimate = 102 * prev_graph_node_count / 100 + 200; - let new_node_dbg = - session.opts.unstable_opts.incremental_verify_ich || cfg!(debug_assertions); - CurrentDepGraph { encoder: GraphEncoder::new(session, encoder, prev_graph_node_count, previous), anon_node_to_index: ShardedHashMap::with_capacity( @@ -1174,12 +1157,6 @@ impl CurrentDepGraph { forbidden_edge, #[cfg(debug_assertions)] value_fingerprints: Lock::new(IndexVec::from_elem_n(None, new_node_count_estimate)), - nodes_in_current_session: new_node_dbg.then(|| { - Lock::new(FxHashMap::with_capacity_and_hasher( - new_node_count_estimate, - Default::default(), - )) - }), total_read_count: AtomicU64::new(0), total_duplicate_read_count: AtomicU64::new(0), } @@ -1202,25 +1179,6 @@ impl CurrentDepGraph { assert_eq!(prior_value_fingerprint, value_fingerprint, "Unstable fingerprints for {key:?}"); } - #[inline(always)] - fn record_node( - &self, - dep_node_index: DepNodeIndex, - key: DepNode, - _value_fingerprint: Fingerprint, - ) { - #[cfg(debug_assertions)] - self.record_edge(dep_node_index, key, _value_fingerprint); - - if let Some(ref nodes_in_current_session) = self.nodes_in_current_session { - outline(|| { - if nodes_in_current_session.lock().insert(key, dep_node_index).is_some() { - panic!("Found duplicate dep-node {key:?}"); - } - }); - } - } - /// Writes the node to the current dep-graph and allocates a `DepNodeIndex` for it. /// Assumes that this is a node that has no equivalent in the previous dep-graph. #[inline(always)] @@ -1232,28 +1190,11 @@ impl CurrentDepGraph { ) -> DepNodeIndex { let dep_node_index = self.encoder.send_new(key, value_fingerprint, edges); - self.record_node(dep_node_index, key, value_fingerprint); + #[cfg(debug_assertions)] + self.record_edge(dep_node_index, key, value_fingerprint); dep_node_index } - - #[inline] - fn debug_assert_not_in_new_nodes( - &self, - prev_graph: &SerializedDepGraph, - prev_index: SerializedDepNodeIndex, - ) { - if !is_dyn_thread_safe() - && let Some(ref nodes_in_current_session) = self.nodes_in_current_session - { - debug_assert!( - !nodes_in_current_session - .lock() - .contains_key(&prev_graph.index_to_node(prev_index)), - "node from previous graph present in new node collection" - ); - } - } } #[derive(Debug, Clone, Copy)] @@ -1430,17 +1371,6 @@ fn panic_on_forbidden_read(data: &DepGraphData, dep_node_index: DepNodeIndex) -> } } - if dep_node.is_none() - && let Some(nodes) = &data.current.nodes_in_current_session - { - // Try to find it among the nodes allocated so far in this session - // This is OK, there's only ever one node result possible so this is deterministic. - #[allow(rustc::potential_query_instability)] - if let Some((node, _)) = nodes.lock().iter().find(|&(_, index)| *index == dep_node_index) { - dep_node = Some(*node); - } - } - let dep_node = dep_node.map_or_else( || format!("with index {:?}", dep_node_index), |dep_node| format!("`{:?}`", dep_node), diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a65a5fcc6bffe..d1f82af3416b6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -703,6 +703,23 @@ impl<'tcx> TyCtxt<'tcx> { self.dep_graph.assert_ignored(); TyCtxtFeed { tcx: self, key }.delayed_owner(owner); } + + // Trait impl item visibility is inherited from its trait when not specified + // explicitly. In that case we cannot determine it in early resolve, + // but instead are feeding it in late resolve, where we don't have access to the + // `TyCtxtFeed` anymore. + // To avoid having to hash the `LocalDefId` multiple times for inserting and removing the + // `TyCtxtFeed` from a hash table, we add this hack to feed the visibility. + // Do not use outside of the resolver query. + pub fn feed_visibility_for_trait_impl_item(self, key: LocalDefId, vis: ty::Visibility) { + if cfg!(debug_assertions) { + match self.def_kind(self.local_parent(key)) { + DefKind::Impl { of_trait: true } => {} + other => bug!("{key:?} is not an assoc item of a trait impl: {other:?}"), + } + } + TyCtxtFeed { tcx: self, key }.visibility(vis.to_def_id()) + } } impl<'tcx, KEY: Copy> TyCtxtFeed<'tcx, KEY> { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 06dd8cbc089b6..d11825016baf3 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -200,7 +200,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target) }, Attribute::Parsed(AttributeKind::OnUnimplemented{directive,..}) => {self.check_diagnostic_on_unimplemented(hir_id, directive.as_deref())}, - Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)} + Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)}, + Attribute::Parsed(AttributeKind::OnUnmatchArgs { directive, .. }) => { + self.check_diagnostic_on_unmatch_args(hir_id, directive.as_deref()) + }, Attribute::Parsed(AttributeKind::OnMove { directive , .. }) => { self.check_diagnostic_on_move(hir_id, directive.as_deref()) }, @@ -559,6 +562,20 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // ...whose generics would that be, anyway? The traits' or the impls'? } + /// Checks use of generic formatting parameters in `#[diagnostic::on_unmatch_args]`. + fn check_diagnostic_on_unmatch_args(&self, hir_id: HirId, directive: Option<&Directive>) { + if let Some(directive) = directive { + directive.visit_params(&mut |argument_name, span| { + self.tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + hir_id, + span, + errors::OnUnmatchArgsMalformedFormatLiterals { name: argument_name }, + ) + }); + } + } + /// Checks use of generic formatting parameters in `#[diagnostic::on_move]` fn check_diagnostic_on_move(&self, hir_id: HirId, directive: Option<&Directive>) { if let Some(directive) = directive { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index a916b4670fded..5300267573e62 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1303,3 +1303,10 @@ pub(crate) struct UnknownFormatParameterForOnUnimplementedAttr { pub(crate) struct OnMoveMalformedFormatLiterals { pub name: Symbol, } + +#[derive(Diagnostic)] +#[diag("unknown parameter `{$name}`")] +#[help(r#"use {"`{This}`"} to refer to the macro name"#)] +pub(crate) struct OnUnmatchArgsMalformedFormatLiterals { + pub name: Symbol, +} diff --git a/compiler/rustc_public/src/compiler_interface.rs b/compiler/rustc_public/src/compiler_interface.rs index 30fbc5c5233dc..c2f36bea81ca6 100644 --- a/compiler/rustc_public/src/compiler_interface.rs +++ b/compiler/rustc_public/src/compiler_interface.rs @@ -17,11 +17,11 @@ use crate::mir::mono::{Instance, InstanceDef, StaticDef}; use crate::mir::{BinOp, Body, Place, UnOp}; use crate::target::{MachineInfo, MachineSize}; use crate::ty::{ - AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef, - ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, - Generics, ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, - TraitDecl, TraitDef, TraitRef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, VariantIdx, - VtblEntry, + AdtDef, AdtKind, Allocation, Asyncness, ClosureDef, ClosureKind, Constness, CoroutineDef, + Discr, FieldDef, FnDef, ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, + GenericArgs, GenericPredicates, Generics, ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, + PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, TraitRef, Ty, TyConst, TyConstId, TyKind, + UintTy, VariantDef, VariantIdx, VtblEntry, }; use crate::unstable::{RustcInternal, Stable, new_item_kind}; use crate::{ @@ -388,6 +388,22 @@ impl<'tcx> CompilerInterface<'tcx> { cx.fn_sig(def_id, args_ref).stable(&mut *tables, cx) } + /// Retrieve the constness for the given function definition. + pub(crate) fn constness(&self, def: FnDef) -> Constness { + let mut tables = self.tables.borrow_mut(); + let cx = &*self.cx.borrow(); + let def_id = def.0.internal(&mut *tables, cx.tcx); + cx.constness(def_id).stable(&mut *tables, cx) + } + + /// Retrieve the asyncness for the given function definition. + pub(crate) fn asyncness(&self, def: FnDef) -> Asyncness { + let mut tables = self.tables.borrow_mut(); + let cx = &*self.cx.borrow(); + let def_id = def.0.internal(&mut *tables, cx.tcx); + cx.asyncness(def_id).stable(&mut *tables, cx) + } + /// Retrieve the intrinsic definition if the item corresponds one. pub(crate) fn intrinsic(&self, item: DefId) -> Option { let mut tables = self.tables.borrow_mut(); diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 7ec8b688402ad..9b79c92ac2b96 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -710,6 +710,16 @@ impl FnDef { self.as_intrinsic().is_some() } + /// Get the constness of this function definition. + pub fn constness(&self) -> Constness { + with(|cx| cx.constness(*self)) + } + + /// Get the asyncness of this function definition. + pub fn asyncness(&self) -> Asyncness { + with(|cx| cx.asyncness(*self)) + } + /// Get the function signature for this function definition. pub fn fn_sig(&self) -> PolyFnSig { let kind = self.ty().kind(); @@ -1103,6 +1113,30 @@ impl FnSig { } } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Constness { + Const, + NotConst, +} + +impl Constness { + pub fn is_const(self) -> bool { + matches!(self, Constness::Const) + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Asyncness { + Async, + NotAsync, +} + +impl Asyncness { + pub fn is_async(self) -> bool { + matches!(self, Asyncness::Async) + } +} + #[derive(Clone, PartialEq, Eq, Debug, Serialize)] pub enum Abi { Rust, diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 4f3e9f94c599b..ddecfd3c73f48 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -14,8 +14,8 @@ use crate::mir::alloc::AllocId; use crate::mir::mono::{Instance, MonoItem, StaticDef}; use crate::mir::{BinOp, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp}; use crate::ty::{ - Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, - ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, + Abi, AdtDef, Asyncness, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, + Constness, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, GenericArgKind, GenericArgs, IntTy, MirConst, Movability, Pattern, Region, RigidTy, Span, TermKind, TraitRef, Ty, TyConst, UintTy, VariantDef, VariantIdx, }; @@ -637,6 +637,36 @@ impl RustcInternal for Safety { } } } + +impl RustcInternal for Constness { + type T<'tcx> = rustc_hir::Constness; + + fn internal<'tcx>( + &self, + _tables: &mut Tables<'_, BridgeTys>, + _tcx: impl InternalCx<'tcx>, + ) -> Self::T<'tcx> { + match self { + Constness::Const => rustc_hir::Constness::Const, + Constness::NotConst => rustc_hir::Constness::NotConst, + } + } +} + +impl RustcInternal for Asyncness { + type T<'tcx> = rustc_ty::Asyncness; + + fn internal<'tcx>( + &self, + _tables: &mut Tables<'_, BridgeTys>, + _tcx: impl InternalCx<'tcx>, + ) -> Self::T<'tcx> { + match self { + Asyncness::Async => rustc_ty::Asyncness::Yes, + Asyncness::NotAsync => rustc_ty::Asyncness::No, + } + } +} impl RustcInternal for Span { type T<'tcx> = rustc_span::Span; diff --git a/compiler/rustc_public/src/unstable/convert/stable/mod.rs b/compiler/rustc_public/src/unstable/convert/stable/mod.rs index 4d550487525f3..ce55e898b2acb 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mod.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mod.rs @@ -21,6 +21,26 @@ impl<'tcx> Stable<'tcx> for rustc_hir::Safety { } } +impl<'tcx> Stable<'tcx> for rustc_hir::Constness { + type T = crate::ty::Constness; + fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { + match self { + rustc_hir::Constness::Const => crate::ty::Constness::Const, + rustc_hir::Constness::NotConst => crate::ty::Constness::NotConst, + } + } +} + +impl<'tcx> Stable<'tcx> for rustc_middle::ty::Asyncness { + type T = crate::ty::Asyncness; + fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { + match self { + rustc_middle::ty::Asyncness::Yes => crate::ty::Asyncness::Async, + rustc_middle::ty::Asyncness::No => crate::ty::Asyncness::NotAsync, + } + } +} + impl<'tcx> Stable<'tcx> for FieldIdx { type T = usize; fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index 1df2b1fa64285..bb504bd0017a0 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -383,6 +383,16 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { sig } + /// Retrieve the constness for the given function definition. + pub fn constness(&self, def_id: DefId) -> rustc_hir::Constness { + self.tcx.constness(def_id) + } + + /// Retrieve the asyncness for the given function definition. + pub fn asyncness(&self, def_id: DefId) -> ty::Asyncness { + self.tcx.asyncness(def_id) + } + /// Retrieve the intrinsic definition if the item corresponds one. pub fn intrinsic(&self, def_id: DefId) -> Option { let intrinsic = self.tcx.intrinsic_raw(def_id); diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 4148ec1b7abd7..7482bd3283c8c 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -21,7 +21,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_index::bit_set::DenseBitSet; use rustc_metadata::creader::LoadedMacro; use rustc_middle::metadata::{ModChild, Reexport}; -use rustc_middle::ty::{Feed, Visibility}; +use rustc_middle::ty::{TyCtxtFeed, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind}; use rustc_span::{Ident, Span, Symbol, kw, sym}; @@ -155,7 +155,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // Query `def_kind` is not used because query system overhead is too expensive here. - let def_kind = self.cstore().def_kind_untracked(self.tcx, def_id); + let def_kind = self.cstore().def_kind_untracked(def_id); if def_kind.is_module_like() { let parent = self.tcx.opt_parent(def_id).map(|parent_id| { self.get_nearest_non_block_module(parent_id).expect_extern() @@ -563,6 +563,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { item: &Item, vis: Visibility, root_span: Span, + feed: TyCtxtFeed<'tcx, LocalDefId>, ) { debug!( "build_reduced_graph_for_use_tree(parent_prefix={:?}, use_tree={:?}, nested={})", @@ -572,7 +573,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { // Top level use tree reuses the item's id and list stems reuse their parent // use tree's ids, so in both cases their visibilities are already filled. if nested && !list_stem { - self.r.feed_visibility(self.r.feed(id), vis); + self.r.feed_visibility(feed, vis); } let mut prefix_iter = parent_prefix @@ -735,11 +736,11 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } ast::UseTreeKind::Nested { ref items, .. } => { for &(ref tree, id) in items { - self.create_def(id, None, DefKind::Use, use_tree.span()); + let feed = self.create_def(id, None, DefKind::Use, use_tree.span()); self.build_reduced_graph_for_use_tree( // This particular use tree tree, id, &prefix, true, false, // The whole `use` item - item, vis, root_span, + item, vis, root_span, feed, ); } @@ -768,6 +769,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { self.parent_scope.module.nearest_parent_mod().expect_local(), ), root_span, + feed, ); } } @@ -778,7 +780,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { &mut self, fields: &[ast::FieldDef], ident: Ident, - feed: Feed<'tcx, LocalDefId>, + feed: TyCtxtFeed<'tcx, LocalDefId>, adt_res: Res, adt_vis: Visibility, adt_span: Span, @@ -798,13 +800,12 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } /// Constructs the reduced graph for one item. - fn build_reduced_graph_for_item(&mut self, item: &'a Item) { + fn build_reduced_graph_for_item(&mut self, item: &'a Item, feed: TyCtxtFeed<'tcx, LocalDefId>) { let parent_scope = &self.parent_scope; let parent = parent_scope.module.expect_local(); let expansion = parent_scope.expansion; let sp = item.span; let vis = self.resolve_visibility(&item.vis); - let feed = self.r.feed(item.id); let local_def_id = feed.key(); let def_id = local_def_id.to_def_id(); let def_kind = self.r.tcx.def_kind(def_id); @@ -825,6 +826,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { item, vis, use_tree.span(), + feed, ); } @@ -867,7 +869,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { // Functions introducing procedural macros reserve a slot // in the macro namespace as well (see #52225). - self.define_macro(item); + self.define_macro(item, feed); } // These items live in the type namespace. @@ -902,7 +904,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { // If this is a tuple or unit struct, define a name // in the value namespace as well. - if let Some(ctor_node_id) = vdata.ctor_node_id() { + if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(vdata) { // If the structure is marked as non_exhaustive then lower the visibility // to within the crate. let mut ctor_vis = if vis.is_public() @@ -927,7 +929,14 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } field_visibilities.push(field_vis.to_def_id()); } - let feed = self.r.feed(ctor_node_id); + // If this is a unit or tuple-like struct, register the constructor. + let feed = self.create_def( + ctor_node_id, + None, + DefKind::Ctor(CtorOf::Struct, ctor_kind), + item.span, + ); + let ctor_def_id = feed.key(); let ctor_res = self.res(ctor_def_id); self.r.define_local(parent, ident, ValueNS, ctor_res, ctor_vis, sp, expansion); @@ -1062,8 +1071,8 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { &mut self, item: &ForeignItem, ident: Ident, + feed: TyCtxtFeed<'tcx, LocalDefId>, ) { - let feed = self.r.feed(item.id); let local_def_id = feed.key(); let def_id = local_def_id.to_def_id(); let ns = match item.kind { @@ -1259,10 +1268,13 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } } - fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScopeRef<'ra> { + fn define_macro( + &mut self, + item: &ast::Item, + feed: TyCtxtFeed<'tcx, LocalDefId>, + ) -> MacroRulesScopeRef<'ra> { let parent_scope = self.parent_scope; let expansion = parent_scope.expansion; - let feed = self.r.feed(item.id); let def_id = feed.key(); let (res, orig_ident, span, macro_rules) = match &item.kind { ItemKind::MacroDef(ident, def) => { @@ -1361,18 +1373,17 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { - pub(crate) fn brg_visit_item(&mut self, item: &'a Item) { + pub(crate) fn brg_visit_item(&mut self, item: &'a Item, feed: TyCtxtFeed<'tcx, LocalDefId>) { let orig_module_scope = self.parent_scope.module; self.parent_scope.macro_rules = match item.kind { ItemKind::MacroDef(..) => { - let macro_rules_scope = self.define_macro(item); + let macro_rules_scope = self.define_macro(item, feed); visit::walk_item(self, item); macro_rules_scope } - ItemKind::MacCall(..) => self.visit_invoc_in_module(item.id), _ => { let orig_macro_rules_scope = self.parent_scope.macro_rules; - self.build_reduced_graph_for_item(item); + self.build_reduced_graph_for_item(item, feed); match item.kind { ItemKind::Mod(..) => { // Visit attributes after items for backward compatibility. @@ -1394,8 +1405,10 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { self.parent_scope.module = orig_module_scope; } - pub(crate) fn brg_visit_stmt_mac_call(&mut self, stmt: &'a ast::Stmt) { - self.parent_scope.macro_rules = self.visit_invoc_in_module(stmt.id); + /// Handle a macro call that itself can produce new `macro_rules` items + /// in the current module. + pub(crate) fn brg_visit_mac_call_in_module(&mut self, id: NodeId) { + self.parent_scope.macro_rules = self.visit_invoc_in_module(id); } pub(crate) fn brg_visit_block(&mut self, block: &'a Block) { @@ -1413,9 +1426,9 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { ctxt: AssocCtxt, ident: Ident, ns: Namespace, + feed: TyCtxtFeed<'tcx, LocalDefId>, ) { let vis = self.resolve_visibility(&item.vis); - let feed = self.r.feed(item.id); let local_def_id = feed.key(); let def_id = local_def_id.to_def_id(); @@ -1467,21 +1480,28 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } } - pub(crate) fn brg_visit_field_def(&mut self, sf: &'a ast::FieldDef) { + pub(crate) fn brg_visit_field_def( + &mut self, + sf: &'a ast::FieldDef, + feed: TyCtxtFeed<'tcx, LocalDefId>, + ) { let vis = self.resolve_visibility(&sf.vis); - self.r.feed_visibility(self.r.feed(sf.id), vis); + self.r.feed_visibility(feed, vis); visit::walk_field_def(self, sf); } // Constructs the reduced graph for one variant. Variants exist in the // type and value namespaces. - pub(crate) fn brg_visit_variant(&mut self, variant: &'a ast::Variant) { + pub(crate) fn brg_visit_variant( + &mut self, + variant: &'a ast::Variant, + feed: TyCtxtFeed<'tcx, LocalDefId>, + ) { let parent = self.parent_scope.module.expect_local(); let expn_id = self.parent_scope.expansion; let ident = variant.ident; // Define a name in the type namespace. - let feed = self.r.feed(variant.id); let def_id = feed.key(); let vis = self.resolve_visibility(&variant.vis); self.r.define_local(parent, ident, TypeNS, self.res(def_id), vis, variant.span, expn_id); @@ -1496,8 +1516,13 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { }; // Define a constructor name in the value namespace. - if let Some(ctor_node_id) = variant.data.ctor_node_id() { - let feed = self.r.feed(ctor_node_id); + if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(&variant.data) { + let feed = self.create_def( + ctor_node_id, + None, + DefKind::Ctor(CtorOf::Variant, ctor_kind), + variant.span, + ); let ctor_def_id = feed.key(); let ctor_res = self.res(ctor_def_id); self.r.define_local(parent, ident, ValueNS, ctor_res, ctor_vis, variant.span, expn_id); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index a9b2ad9223a3c..0730c4e4ac9c5 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -7,10 +7,11 @@ use rustc_attr_parsing::{AttributeParser, Early, OmitDoc, ShouldEmit}; use rustc_expand::expand::AstFragment; use rustc_hir as hir; use rustc_hir::Target; +use rustc_hir::def::DefKind; use rustc_hir::def::Namespace::{TypeNS, ValueNS}; -use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::LocalDefId; use rustc_middle::span_bug; +use rustc_middle::ty::TyCtxtFeed; use rustc_span::{Span, Symbol, sym}; use tracing::{debug, instrument}; @@ -43,22 +44,20 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { name: Option, def_kind: DefKind, span: Span, - ) -> LocalDefId { + ) -> TyCtxtFeed<'tcx, LocalDefId> { let parent_def = self.invocation_parent.parent_def; debug!( "create_def(node_id={:?}, def_kind={:?}, parent_def={:?})", node_id, def_kind, parent_def ); - self.r - .create_def( - parent_def, - node_id, - name, - def_kind, - self.parent_scope.expansion.to_expn_id(), - span.with_parent(None), - ) - .def_id() + self.r.create_def( + parent_def, + node_id, + name, + def_kind, + self.parent_scope.expansion.to_expn_id(), + span.with_parent(None), + ) } fn with_parent(&mut self, parent_def: LocalDefId, f: F) { @@ -100,7 +99,7 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> { } else { let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name); let def = self.create_def(field.id, Some(name), DefKind::Field, field.span); - self.with_parent(def, |this| this.brg_visit_field_def(field)); + self.with_parent(def.def_id(), |this| this.brg_visit_field_def(field, def)); } } @@ -173,42 +172,25 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, ItemKind::Use(_) => { - self.create_def(i.id, None, DefKind::Use, i.span); - self.brg_visit_item(i); + let feed = self.create_def(i.id, None, DefKind::Use, i.span); + self.brg_visit_item(i, feed); return; } - ItemKind::MacCall(..) | ItemKind::DelegationMac(..) => { + ItemKind::MacCall(..) => { self.visit_macro_invoc(i.id); - self.brg_visit_item(i); + self.brg_visit_mac_call_in_module(i.id); return; } + ItemKind::DelegationMac(..) => unreachable!(), }; - let def_id = - self.create_def(i.id, i.kind.ident().map(|ident| ident.name), def_kind, i.span); + let feed = self.create_def(i.id, i.kind.ident().map(|ident| ident.name), def_kind, i.span); if let Some(macro_data) = opt_macro_data { - self.r.new_local_macro(def_id, macro_data); + self.r.new_local_macro(feed.def_id(), macro_data); } - self.with_parent(def_id, |this| { - this.with_impl_trait(ImplTraitContext::Existential, |this| { - match i.kind { - ItemKind::Struct(_, _, ref struct_def) - | ItemKind::Union(_, _, ref struct_def) => { - // If this is a unit or tuple-like struct, register the constructor. - if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(struct_def) { - this.create_def( - ctor_node_id, - None, - DefKind::Ctor(CtorOf::Struct, ctor_kind), - i.span, - ); - } - } - _ => {} - } - this.brg_visit_item(i); - }) + self.with_parent(feed.def_id(), |this| { + this.with_impl_trait(ImplTraitContext::Existential, |this| this.brg_visit_item(i, feed)) }); } @@ -244,15 +226,17 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } let (return_id, return_span) = coroutine_kind.return_id(); - let return_def = self.create_def(return_id, None, DefKind::OpaqueTy, return_span); + let return_def = + self.create_def(return_id, None, DefKind::OpaqueTy, return_span).def_id(); self.with_parent(return_def, |this| this.visit_fn_ret_ty(output)); // If this async fn has no body (i.e. it's an async fn signature in a trait) // then the closure_def will never be used, and we should avoid generating a // def-id for it. if let Some(body) = body { - let closure_def = - self.create_def(coroutine_kind.closure_id(), None, DefKind::Closure, span); + let closure_def = self + .create_def(coroutine_kind.closure_id(), None, DefKind::Closure, span) + .def_id(); self.with_parent(closure_def, |this| this.visit_block(body)); } } @@ -262,8 +246,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { // Async closures desugar to closures inside of closures, so // we must create two defs. - let coroutine_def = - self.create_def(coroutine_kind.closure_id(), None, DefKind::Closure, span); + let coroutine_def = self + .create_def(coroutine_kind.closure_id(), None, DefKind::Closure, span) + .def_id(); self.with_parent(coroutine_def, |this| this.visit_expr(body)); } _ => visit::walk_fn(self, fn_kind), @@ -299,8 +284,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { let def = self.create_def(fi.id, Some(ident.name), def_kind, fi.span); - self.with_parent(def, |this| { - this.build_reduced_graph_for_foreign_item(fi, ident); + self.with_parent(def.def_id(), |this| { + this.build_reduced_graph_for_foreign_item(fi, ident, def); visit::walk_item(this, fi) }); } @@ -311,18 +296,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { self.visit_invoc_in_module(v.id); return; } - let def = self.create_def(v.id, Some(v.ident.name), DefKind::Variant, v.span); - self.with_parent(def, |this| { - if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(&v.data) { - this.create_def( - ctor_node_id, - None, - DefKind::Ctor(CtorOf::Variant, ctor_kind), - v.span, - ); - } - this.brg_visit_variant(v); - }); + let feed = self.create_def(v.id, Some(v.ident.name), DefKind::Variant, v.span); + self.with_parent(feed.def_id(), |this| this.brg_visit_variant(v, feed)); } fn visit_where_predicate(&mut self, pred: &'a WherePredicate) { @@ -391,8 +366,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } }; - let def = self.create_def(i.id, Some(ident.name), def_kind, i.span); - self.with_parent(def, |this| this.brg_visit_assoc_item(i, ctxt, ident, ns)); + let feed = self.create_def(i.id, Some(ident.name), def_kind, i.span); + self.with_parent(feed.def_id(), |this| this.brg_visit_assoc_item(i, ctxt, ident, ns, feed)); } fn visit_pat(&mut self, pat: &'a Pat) { @@ -410,8 +385,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { // to avoid affecting stable we have to feature gate the not creating // anon consts if !self.r.tcx.features().min_generic_const_args() { - let parent = - self.create_def(constant.id, None, DefKind::AnonConst, constant.value.span); + let parent = self + .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) + .def_id(); return self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } @@ -421,8 +397,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { }), MgcaDisambiguation::AnonConst => { self.with_const_arg(ConstArgContext::NonDirect, |this| { - let parent = - this.create_def(constant.id, None, DefKind::AnonConst, constant.value.span); + let parent = this + .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) + .def_id(); this.with_parent(parent, |this| visit::walk_anon_const(this, constant)); }) } @@ -440,7 +417,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { return; } ExprKind::Closure(..) | ExprKind::Gen(..) => { - self.create_def(expr.id, None, DefKind::Closure, expr.span) + self.create_def(expr.id, None, DefKind::Closure, expr.span).def_id() } ExprKind::ConstBlock(constant) => { // Under `min_generic_const_args` a `const { }` block sometimes @@ -455,7 +432,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { visit::walk_attribute(this, attr); } - let def = this.create_def(constant.id, None, def_kind, constant.value.span); + let def = + this.create_def(constant.id, None, def_kind, constant.value.span).def_id(); this.with_parent(def, |this| visit::walk_anon_const(this, constant)); }); } @@ -505,7 +483,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { ImplTraitContext::Existential => DefKind::OpaqueTy, ImplTraitContext::InBinding => return visit::walk_ty(self, ty), }; - let id = self.create_def(opaque_id, Some(name), kind, ty.span); + let id = self.create_def(opaque_id, Some(name), kind, ty.span).def_id(); match self.invocation_parent.impl_trait_context { // Do not nest APIT, as we desugar them as `impl_trait: bounds`, // so the `impl_trait` node is not a parent to `bounds`. @@ -523,7 +501,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_stmt(&mut self, stmt: &'a Stmt) { match stmt.kind { StmtKind::MacCall(..) => { - self.brg_visit_stmt_mac_call(stmt); + self.brg_visit_mac_call_in_module(stmt.id); self.visit_macro_invoc(stmt.id) } // FIXME(impl_trait_in_bindings): We don't really have a good way of @@ -628,12 +606,14 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { } } InlineAsmOperand::Const { anon_const } => { - let def = self.create_def( - anon_const.id, - None, - DefKind::InlineConst, - anon_const.value.span, - ); + let def = self + .create_def( + anon_const.id, + None, + DefKind::InlineConst, + anon_const.value.span, + ) + .def_id(); self.with_parent(def, |this| visit::walk_anon_const(this, anon_const)); } InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym), diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4c20fee4be4f7..6eb5fe17bc169 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3765,7 +3765,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ); Visibility::Public }; - this.r.feed_visibility(this.r.feed(id), vis); + // HACK: because we don't want to track the `TyCtxtFeed` through the resolver to here + // in a hash-map, we instead conjure a `TyCtxtFeed` for any `DefId` here, but prevent + // it from being used generally. + this.r.tcx.feed_visibility_for_trait_impl_item(this.r.local_def_id(id), vis); }; let Some(decl) = decl else { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 47d6988ca5984..cc9ad46c4dca2 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -66,8 +66,8 @@ use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::query::Providers; use rustc_middle::ty::{ - self, DelegationInfo, Feed, MainDefinition, RegisteredTools, ResolverAstLowering, - ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility, + self, DelegationInfo, MainDefinition, RegisteredTools, ResolverAstLowering, ResolverGlobalCtxt, + TyCtxt, TyCtxtFeed, Visibility, }; use rustc_session::config::CrateType; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; @@ -1422,7 +1422,7 @@ pub struct Resolver<'ra, 'tcx> { next_node_id: NodeId = CRATE_NODE_ID, - node_id_to_def_id: NodeMap>, + node_id_to_def_id: NodeMap, disambiguators: LocalDefIdMap, @@ -1589,19 +1589,11 @@ impl<'ra, 'tcx> AsRef> for Resolver<'ra, 'tcx> { impl<'tcx> Resolver<'_, 'tcx> { fn opt_local_def_id(&self, node: NodeId) -> Option { - self.opt_feed(node).map(|f| f.key()) - } - - fn local_def_id(&self, node: NodeId) -> LocalDefId { - self.feed(node).key() - } - - fn opt_feed(&self, node: NodeId) -> Option> { self.node_id_to_def_id.get(&node).copied() } - fn feed(&self, node: NodeId) -> Feed<'tcx, LocalDefId> { - self.opt_feed(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`")) + fn local_def_id(&self, node: NodeId) -> LocalDefId { + self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`")) } fn local_def_kind(&self, node: NodeId) -> DefKind { @@ -1624,7 +1616,7 @@ impl<'tcx> Resolver<'_, 'tcx> { node_id, name, def_kind, - self.tcx.definitions_untracked().def_key(self.node_id_to_def_id[&node_id].key()), + self.tcx.definitions_untracked().def_key(self.node_id_to_def_id[&node_id]), ); let disambiguator = self.disambiguators.get_or_create(parent); @@ -1648,7 +1640,7 @@ impl<'tcx> Resolver<'_, 'tcx> { // we don't need a mapping from `NodeId` to `LocalDefId`. if node_id != ast::DUMMY_NODE_ID { debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id); - self.node_id_to_def_id.insert(node_id, feed.downgrade()); + self.node_id_to_def_id.insert(node_id, def_id); } feed @@ -1699,7 +1691,7 @@ impl<'tcx> Resolver<'_, 'tcx> { fn def_id_to_node_id(&self, def_id: LocalDefId) -> NodeId { self.node_id_to_def_id .items() - .filter(|(_, v)| v.key() == def_id) + .filter(|(_, v)| **v == def_id) .map(|(k, _)| *k) .get_only() .unwrap() @@ -1740,8 +1732,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let crate_feed = tcx.create_local_crate_def_id(crate_span); crate_feed.def_kind(DefKind::Mod); - let crate_feed = crate_feed.downgrade(); - node_id_to_def_id.insert(CRATE_NODE_ID, crate_feed); + node_id_to_def_id.insert(CRATE_NODE_ID, CRATE_DEF_ID); let mut invocation_parents = FxHashMap::default(); invocation_parents.insert(LocalExpnId::ROOT, InvocationParent::ROOT); @@ -1894,8 +1885,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Default::default() } - fn feed_visibility(&mut self, feed: Feed<'tcx, LocalDefId>, vis: Visibility) { - let feed = feed.upgrade(self.tcx); + fn feed_visibility(&mut self, feed: TyCtxtFeed<'tcx, LocalDefId>, vis: Visibility) { feed.visibility(vis.to_def_id()); self.visibilities_for_hashing.push((feed.def_id(), vis)); } @@ -1914,8 +1904,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .stripped_cfg_items .into_iter() .filter_map(|item| { - let parent_scope = - self.node_id_to_def_id.get(&item.parent_scope)?.key().to_def_id(); + let parent_scope = self.node_id_to_def_id.get(&item.parent_scope)?.to_def_id(); Some(StrippedCfgItem { parent_scope, ident: item.ident, cfg: item.cfg }) }) .collect(); @@ -1945,11 +1934,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { lifetimes_res_map: self.lifetimes_res_map, extra_lifetime_params_map: self.extra_lifetime_params_map, next_node_id: self.next_node_id, - node_id_to_def_id: self - .node_id_to_def_id - .into_items() - .map(|(k, f)| (k, f.key())) - .collect(), + node_id_to_def_id: self.node_id_to_def_id, trait_map: self.trait_map, lifetime_elision_allowed: self.lifetime_elision_allowed, lint_buffer: Steal::new(self.lint_buffer), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index c53763f920468..dc20d4fcf36b7 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -710,6 +710,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (sym::on_move, Some(sym::diagnostic_on_move)), (sym::on_const, Some(sym::diagnostic_on_const)), (sym::on_unknown, Some(sym::diagnostic_on_unknown)), + (sym::on_unmatch_args, Some(sym::diagnostic_on_unmatch_args)), ]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 981bfed363dcc..e7574eaed5b27 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -803,6 +803,7 @@ symbols! { diagnostic_on_const, diagnostic_on_move, diagnostic_on_unknown, + diagnostic_on_unmatch_args, dialect, direct, discriminant_kind, @@ -1427,6 +1428,7 @@ symbols! { on_move, on_unimplemented, on_unknown, + on_unmatch_args, opaque, opaque_module_name_placeholder: "", ops, diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 0e537e2f92fcf..120d7a9f4760c 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -45,6 +45,9 @@ impl Clone /// variant must also be specified. Only a single field is supported. #[unstable(feature = "field_projections", issue = "145383")] #[allow_internal_unstable(field_representing_type_raw, builtin_syntax)] +#[diagnostic::on_unmatch_args( + note = "this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)`" +)] // NOTE: when stabilizing this macro, we can never add new trait impls for `FieldRepresentingType`, // since it is `#[fundamental]` and thus could break users of this macro, since the compiler expands // it to `FieldRepresentingType<...>`. Thus stabilizing this requires careful thought about the diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index bdc1c48f70dfe..2b4ac66212e5b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -131,6 +131,7 @@ #![feature(deprecated_suggestion)] #![feature(derive_const)] #![feature(diagnostic_on_const)] +#![feature(diagnostic_on_unmatch_args)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] #![feature(extern_types)] diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 91934cfceb4e3..2fbb5842ef778 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1567,6 +1567,9 @@ impl SizedTypeProperties for T {} /// [`offset_of_enum`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-enum.html /// [`offset_of_slice`]: https://doc.rust-lang.org/nightly/unstable-book/language-features/offset-of-slice.html #[stable(feature = "offset_of", since = "1.77.0")] +#[diagnostic::on_unmatch_args( + note = "this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)`" +)] #[allow_internal_unstable(builtin_syntax, core_intrinsics)] pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) { // The `{}` is for better error messages diff --git a/library/core/src/time.rs b/library/core/src/time.rs index a5b654033ba14..9073131b35b61 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -1006,6 +1006,7 @@ impl Duration { /// This method will panic if result is negative, overflows `Duration` or not finite. /// /// # Examples + /// /// ``` /// use std::time::Duration; /// @@ -1013,6 +1014,37 @@ impl Duration { /// assert_eq!(dur.mul_f64(3.14), Duration::new(8, 478_000_000)); /// assert_eq!(dur.mul_f64(3.14e5), Duration::new(847_800, 0)); /// ``` + /// + /// Note that `f64` does not have enough bits ([`f64::MANTISSA_DIGITS`]) to represent the full + /// range of possible `Duration` with nanosecond precision, so rounding may occur even for + /// trivial operations like multiplying by 1. + /// + /// ``` + /// # #![feature(float_exact_integer_constants)] + /// use std::time::Duration; + /// + /// // This is about 14.9 weeks, remaining precise to the nanosecond: + /// let weeks = Duration::from_nanos(f64::MAX_EXACT_INTEGER as u64); + /// assert_eq!(weeks, weeks.mul_f64(1.0)); + /// + /// // A larger value incurs rounding in the floating-point operation: + /// let weeks = Duration::from_nanos(u64::MAX); + /// assert_ne!(weeks, weeks.mul_f64(1.0)); + /// + /// // This is over 285 million years, remaining precise to the second: + /// let years = Duration::from_secs(f64::MAX_EXACT_INTEGER as u64); + /// assert_eq!(years, years.mul_f64(1.0)); + /// + /// // And again larger values incur rounding: + /// let years = Duration::from_secs(u64::MAX / 2); + /// assert_ne!(years, years.mul_f64(1.0)); + /// ``` + /// + /// ```should_panic + /// # use std::time::Duration; + /// // In the extreme, rounding can even overflow `Duration`, which panics. + /// let _ = Duration::from_secs(u64::MAX).mul_f64(1.0); + /// ``` #[stable(feature = "duration_float", since = "1.38.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] @@ -1023,6 +1055,10 @@ impl Duration { /// Multiplies `Duration` by `f32`. /// + /// Since the significand of `f32` is quite limited compared to the range of `Duration` + /// -- only about 16.8ms of exact nanosecond precision -- this method currently forwards + /// to [`mul_f64`][Self::mul_f64] for greater accuracy. + /// /// # Panics /// This method will panic if result is negative, overflows `Duration` or not finite. /// @@ -1031,7 +1067,10 @@ impl Duration { /// use std::time::Duration; /// /// let dur = Duration::new(2, 700_000_000); - /// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_641)); + /// // Note that this `3.14_f32` argument already has more floating-point + /// // representation error than a direct `3.14_f64` would, so the result + /// // is slightly different from the ideal 8.478s. + /// assert_eq!(dur.mul_f32(3.14), Duration::new(8, 478_000_283)); /// assert_eq!(dur.mul_f32(3.14e5), Duration::new(847_800, 0)); /// ``` #[stable(feature = "duration_float", since = "1.38.0")] @@ -1039,7 +1078,7 @@ impl Duration { without modifying the original"] #[inline] pub fn mul_f32(self, rhs: f32) -> Duration { - Duration::from_secs_f32(rhs * self.as_secs_f32()) + self.mul_f64(rhs.into()) } /// Divides `Duration` by `f64`. @@ -1048,6 +1087,7 @@ impl Duration { /// This method will panic if result is negative, overflows `Duration` or not finite. /// /// # Examples + /// /// ``` /// use std::time::Duration; /// @@ -1055,6 +1095,37 @@ impl Duration { /// assert_eq!(dur.div_f64(3.14), Duration::new(0, 859_872_611)); /// assert_eq!(dur.div_f64(3.14e5), Duration::new(0, 8_599)); /// ``` + /// + /// Note that `f64` does not have enough bits ([`f64::MANTISSA_DIGITS`]) to represent the full + /// range of possible `Duration` with nanosecond precision, so rounding may occur even for + /// trivial operations like dividing by 1. + /// + /// ``` + /// # #![feature(float_exact_integer_constants)] + /// use std::time::Duration; + /// + /// // This is about 14.9 weeks, remaining precise to the nanosecond: + /// let weeks = Duration::from_nanos(f64::MAX_EXACT_INTEGER as u64); + /// assert_eq!(weeks, weeks.div_f64(1.0)); + /// + /// // A larger value incurs rounding in the floating-point operation: + /// let weeks = Duration::from_nanos(u64::MAX); + /// assert_ne!(weeks, weeks.div_f64(1.0)); + /// + /// // This is over 285 million years, remaining precise to the second: + /// let years = Duration::from_secs(f64::MAX_EXACT_INTEGER as u64); + /// assert_eq!(years, years.div_f64(1.0)); + /// + /// // And again larger values incur rounding: + /// let years = Duration::from_secs(u64::MAX / 2); + /// assert_ne!(years, years.div_f64(1.0)); + /// ``` + /// + /// ```should_panic + /// # use std::time::Duration; + /// // In the extreme, rounding can even overflow `Duration`, which panics. + /// let _ = Duration::from_secs(u64::MAX).div_f64(1.0); + /// ``` #[stable(feature = "duration_float", since = "1.38.0")] #[must_use = "this returns the result of the operation, \ without modifying the original"] @@ -1065,6 +1136,10 @@ impl Duration { /// Divides `Duration` by `f32`. /// + /// Since the significand of `f32` is quite limited compared to the range of `Duration` + /// -- only about 16.8ms of exact nanosecond precision -- this method currently forwards + /// to [`div_f64`][Self::div_f64] for greater accuracy. + /// /// # Panics /// This method will panic if result is negative, overflows `Duration` or not finite. /// @@ -1073,9 +1148,10 @@ impl Duration { /// use std::time::Duration; /// /// let dur = Duration::new(2, 700_000_000); - /// // note that due to rounding errors result is slightly - /// // different from 0.859_872_611 - /// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_580)); + /// // Note that this `3.14_f32` argument already has more floating-point + /// // representation error than a direct `3.14_f64` would, so the result + /// // is slightly different from the ideally rounded 0.859_872_611. + /// assert_eq!(dur.div_f32(3.14), Duration::new(0, 859_872_583)); /// assert_eq!(dur.div_f32(3.14e5), Duration::new(0, 8_599)); /// ``` #[stable(feature = "duration_float", since = "1.38.0")] @@ -1083,7 +1159,7 @@ impl Duration { without modifying the original"] #[inline] pub fn div_f32(self, rhs: f32) -> Duration { - Duration::from_secs_f32(self.as_secs_f32() / rhs) + self.div_f64(rhs.into()) } /// Divides `Duration` by `Duration` and returns `f64`. diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index ac3099d845931..1175e3372ab63 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -30d0309fa821f7a0984a9629e0d227ca3c0d2eda +cf1817bc6ecd2d14ca492247c804bad31948dd56 diff --git a/src/doc/rustc-dev-guide/src/about-this-guide.md b/src/doc/rustc-dev-guide/src/about-this-guide.md index 4dc453a2042de..56fbc1f6b7a85 100644 --- a/src/doc/rustc-dev-guide/src/about-this-guide.md +++ b/src/doc/rustc-dev-guide/src/about-this-guide.md @@ -98,7 +98,7 @@ You might also find the following sites useful: [gsearchdocs]: https://www.google.com/search?q=site:doc.rust-lang.org+your+query+here [stddocs]: https://doc.rust-lang.org/std [rif]: http://internals.rust-lang.org -[rr]: https://doc.rust-lang.org/book/ +[rr]: https://doc.rust-lang.org/reference/ [rustforge]: https://forge.rust-lang.org/ [tlgba]: https://tomlee.co/2014/04/a-more-detailed-tour-of-the-rust-compiler/ [ro]: https://www.rustaceans.org/ diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index 959d0687b7fb5..d9921cbb133ea 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -144,12 +144,19 @@ If running `./x check` on save is inconvenient, in VS Code you can use a [Build ### Neovim -For Neovim users, there are a few options. -The easiest way is by using [neoconf.nvim](https://github.com/folke/neoconf.nvim/), -which allows for project-local configuration files with the native LSP. -The steps for how to use it are below. -Note that they require rust-analyzer to already be configured with Neovim. -Steps for this can be [found here](https://rust-analyzer.github.io/manual.html#nvim-lsp). +For Neovim users, there are a few options: + +1. The easiest way is using [neoconf.nvim][neoconf.nvim] but it uses the + deprecated `require('lspconfig')` API which displays a warning on neovim 0.11+. +2. Using `coc.nvim` is another option but it requires node.js to be installed. +3. Using a custom script to load rust-analyzer settings. + +#### neoconf.nvim + +[neoconf.nvim][neoconf.nvim] allows for project-local configuration +files with the native LSP. The steps for how to use it are below. Note that they require +rust-analyzer to already be configured with Neovim. Steps for this can be +[found here][r-a nvim lsp]. 1. First install the plugin. This can be done by following the steps in the README. @@ -157,64 +164,62 @@ Steps for this can be [found here](https://rust-analyzer.github.io/manual.html#n `neoconf` is able to read and update rust-analyzer settings automatically when the project is opened when this file is detected. +#### coc.nvim + If you're using `coc.nvim`, you can run `./x setup editor` and select `vim` to create a `.vim/coc-settings.json`. The settings can be edited with `:CocLocalConfig`. The recommended settings live at [`src/etc/rust_analyzer_settings.json`]. -Another way is without a plugin, and creating your own logic in your configuration. -The following code will work for any checkout of rust-lang/rust (newer than February 2025): +#### Custom LSP settings + +If you're running neovim 0.11+, you can configure rust-analyzer with just +[nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) and a custom script. + +1. Make sure [rust-analyzer LSP][r-a nvim lsp] is set up +2. Create `$HOME/.config/nvim/after/plugged/rust_analyzer.lua` with the following content: ```lua -local function expand_config_variables(option) - local var_placeholders = { - ['${workspaceFolder}'] = function(_) - return vim.lsp.buf.list_workspace_folders()[1] - end, - } - - if type(option) == "table" then - local mt = getmetatable(option) - local result = {} - for k, v in pairs(option) do - result[expand_config_variables(k)] = expand_config_variables(v) +-- Capture the default functions from nvim-lspconfig/lsp/rust_analyzer.lua before overriding it. +-- This file is in after/plugin to guarantee nvim-lspconfig has been initialised already. +local default_root_dir = vim.lsp.config["rust_analyzer"].root_dir +local default_before_init = vim.lsp.config["rust_analyzer"].before_init + +vim.lsp.config("rust_analyzer", { + cmd = { "rust-analyzer" }, + filetypes = { "rust" }, + -- To support rust-lang/rust, we need to detect when we're in the rust repo and use the git root + -- instead of cargo project root. + root_dir = function(bufnr, on_dir) + local git_root = vim.fs.root(bufnr, { ".git" }) + if git_root then + if vim.uv.fs_stat(vim.fs.joinpath(git_root, "src/etc/rust_analyzer_zed.json")) then + on_dir(git_root) + return + end end - return setmetatable(result, mt) - end - if type(option) ~= "string" then - return option - end - local ret = option - for key, fn in pairs(var_placeholders) do - ret = ret:gsub(key, fn) - end - return ret -end -lspconfig.rust_analyzer.setup { - root_dir = function() - local default = lspconfig.rust_analyzer.config_def.default_config.root_dir() - -- the default root detection uses the cargo workspace root. - -- but for rust-lang/rust, the standard library is in its own workspace. - -- use the git root instead. - local compiler_config = vim.fs.joinpath(default, "../src/bootstrap/defaults/config.compiler.toml") - if vim.fs.basename(default) == "library" and vim.uv.fs_stat(compiler_config) then - return vim.fs.dirname(default) - end - return default + -- For anything that doesn't match rust-lang/rust, fallback to default root_dir + default_root_dir(bufnr, on_dir) end, - on_init = function(client) - local path = client.workspace_folders[1].name - local config = vim.fs.joinpath(path, "src/etc/rust_analyzer_zed.json") - if vim.uv.fs_stat(config) then - -- load rust-lang/rust settings - local file = io.open(config) - local json = vim.json.decode(file:read("*a")) - client.config.settings["rust-analyzer"] = expand_config_variables(json.lsp["rust-analyzer"].initialization_options) - client.notify("workspace/didChangeConfiguration", { settings = client.config.settings }) + before_init = function(init_params, config) + -- When inside rust-lang/rust, we need to use the special rust-analyzer settings. + local settings = vim.fs.joinpath(config.root_dir, "src/etc/rust_analyzer_zed.json") + if vim.uv.fs_stat(settings) then + local file = io.open(settings) + -- nvim 0.12+ supports comments otherwise you'll need content:gsub("//[^\n]*", ""). + local json = vim.json.decode(file:read("*a"), { skip_comments = true }) + file:close() + config.settings["rust-analyzer"] = vim.tbl_deep_extend( + "force", -- Overwrite with the special settings when there is a conflict. + config.settings["rust-analyzer"] or {}, + json.lsp["rust-analyzer"].initialization_options + ) end - return true - end -} + default_before_init(init_params, config) + end, +}) + +vim.lsp.enable("rust_analyzer") ``` If you would like to use the build task that is described above, you may either @@ -223,6 +228,9 @@ make your own command in your config, or you can install a plugin such as files](https://github.com/stevearc/overseer.nvim/blob/master/doc/guides.md#vs-code-tasks), and follow the same instructions as above. +[neoconf.nvim]: https://github.com/folke/neoconf.nvim/ +[r-a nvim lsp]: https://rust-analyzer.github.io/book/other_editors.html#nvim-lsp + ### Emacs Emacs provides support for rust-analyzer with project-local configuration diff --git a/src/doc/rustc-dev-guide/src/coherence.md b/src/doc/rustc-dev-guide/src/coherence.md index 73f9213bf4056..6e4e38b9f7949 100644 --- a/src/doc/rustc-dev-guide/src/coherence.md +++ b/src/doc/rustc-dev-guide/src/coherence.md @@ -8,13 +8,14 @@ Coherence checking is what detects both of trait impls and inherent impls overla Overlapping trait impls always produce an error, while overlapping inherent impls result in an error only if they have methods with the same name. -Checking for overlaps is split in two parts. First there's the [overlap check(s)](#overlap-checks), +Checking for overlaps is split in two parts. +First there's the [overlap check(s)](#overlap-checks), which finds overlaps between traits and inherent implementations that the compiler currently knows about. However, Coherence also results in an error if any other impls **could** exist, -even if they are currently unknown. +even if they are currently unknown. This affects impls which may get added to upstream crates in a backwards compatible way, -and impls from downstream crates. +and impls from downstream crates. This is called the Orphan check. ## Overlap checks @@ -25,7 +26,7 @@ Overlap checks always consider pairs of implementations, comparing them to each Overlap checking for inherent impl blocks is done through `fn check_item` (in coherence/inherent_impls_overlap.rs), where you can very clearly see that (at least for small `n`), the check really performs `n^2` -comparisons between impls. +comparisons between impls. In the case of traits, this check is currently done as part of building the [specialization graph](traits/specialization.md), to handle specializing impls overlapping with their parent, but this may change in the future. @@ -37,7 +38,7 @@ Overlapping is sometimes partially allowed: 1. for marker traits 2. under [specialization](traits/specialization.md) -but normally isn't. +It normally isn't. The overlap check has various modes (see [`OverlapMode`]). Importantly, there's the explicit negative impl check, and the implicit negative impl check. @@ -47,9 +48,9 @@ Both try to prove that an overlap is definitely impossible. ### The explicit negative impl check -This check is done in [`impl_intersection_has_negative_obligation`]. +This check is done in [`impl_intersection_has_negative_obligation`]. -This check tries to find a negative trait implementation. +This check tries to find a negative trait implementation. For example: ```rust @@ -64,7 +65,7 @@ In this example, we'd get: `MyCustomErrorType: From<&str>` and `MyCustomErrorType: From`, giving `?E = &str`. And thus, these two implementations would overlap. -However, libstd provides `&str: !Error`, and therefore guarantees that there +However, libstd provides `&str: !Error`, and therefore guarantees that there will never be a positive implementation of `&str: Error`, and thus there is no overlap. Note that for this kind of negative impl check, we must have explicit negative implementations provided. @@ -77,13 +78,13 @@ This is not currently stable. This check is done in [`impl_intersection_has_impossible_obligation`], and does not rely on negative trait implementations and is stable. -Let's say there's a +Let's say there's a ```rust impl From for Box {} // in your own crate impl From for Box where E: Error {} // in std ``` -This would give: `Box: From`, and `Box: From`, +This would give: `Box: From`, and `Box: From`, giving `?E = MyLocalType`. In your crate there's no `MyLocalType: Error`, downstream crates cannot implement `Error` (a remote trait) for `MyLocalType` (a remote type). @@ -91,4 +92,3 @@ Therefore, these two impls do not overlap. Importantly, this works even if there isn't a `impl !Error for MyLocalType`. [`impl_intersection_has_impossible_obligation`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_trait_selection/traits/coherence/fn.impl_intersection_has_impossible_obligation.html - diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md index 26530a30d066b..edbb1688e3da5 100644 --- a/src/doc/rustc-dev-guide/src/effects.md +++ b/src/doc/rustc-dev-guide/src/effects.md @@ -2,16 +2,16 @@ ## The `HostEffect` predicate -[`HostEffectPredicate`]s are a kind of predicate from `~const Tr` or `const Tr` bounds. +[`HostEffectPredicate`]s are a kind of predicate from `[const] Tr` or `const Tr` bounds. It has a trait reference, and a `constness` which could be `Maybe` or `Const` depending on the bound. -Because `~const Tr`, or rather `Maybe` bounds +Because `[const] Tr`, or rather `Maybe` bounds apply differently based on whichever contexts they are in, they have different behavior than normal bounds. Where normal trait bounds on a function such as `T: Tr` are collected within the [`predicates_of`] query to be proven when a function is called and to be assumed within the function, bounds such as -`T: ~const Tr` will behave as a normal trait bound and add `T: Tr` to the result +`T: [const] Tr` will behave as a normal trait bound and add `T: Tr` to the result from `predicates_of`, but also adds a `HostEffectPredicate` to the [`const_conditions`] query. On the other hand, `T: const Tr` bounds do not change meaning across contexts, @@ -37,7 +37,7 @@ In a similar vein, an item *in const contexts*. If we adjust the example above to use `const` trait bounds: ```rust -const fn foo() where T: ~const Default {} +const fn foo() where T: [const] Default {} ``` Then `foo` would get a `HostEffect(T: Default, maybe)` in the `const_conditions` @@ -55,7 +55,7 @@ Note that we don't check if the function is only referred to but not called, as the following code needs to compile: ```rust -const fn hi() -> T { +const fn hi() -> T { T::default() } const X: fn() -> u32 = hi::; @@ -69,7 +69,7 @@ Here's an example: ```rust const trait Bar {} -const trait Foo: ~const Bar {} +const trait Foo: [const] Bar {} // `const_conditions` contains `HostEffect(Self: Bar, maybe)` impl const Bar for () {} @@ -86,13 +86,13 @@ We do the same for `const_conditions`: ```rust const trait Foo { - fn hi(); + fn hi(); } -impl Foo for Vec { - fn hi(); - // ^ we can't prove `T: ~const PartialEq` given `T: ~const Clone` and - // `T: ~const Default`, therefore we know that the method on the impl +impl Foo for Vec { + fn hi(); + // ^ we can't prove `T: [const] PartialEq` given `T: [const] Clone` and + // `T: [const] Default`, therefore we know that the method on the impl // is stricter than the method on the trait. } ``` @@ -117,11 +117,11 @@ Bounds on associated types, opaque types, and supertraits such as the following have their bounds represented differently: ```rust -trait Foo: ~const PartialEq { - type X: ~const PartialEq; +trait Foo: [const] PartialEq { + type X: [const] PartialEq; } -fn foo() -> impl ~const PartialEq { +fn foo() -> impl [const] PartialEq { // ^ unimplemented syntax } ``` diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index faa2d8f2a3a21..5974e0e92cad6 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -385,8 +385,8 @@ the history becomes easier to work with. The easiest way to squash your commits in a PR on the `rust-lang/rust` repository is to use the `@bors squash` command in a comment on the PR. By default, [bors] combines all commit messages of the PR into the squashed commit message. -To customize the commit message, use `@bors squash msg=`. - +To customize the commit message, use `@bors squash msg=""`. +For example, `@bors squash msg="Improve diagnostics for missing lifetime parameter"`. If you want to squash commits using local git operations, read on below. diff --git a/src/doc/rustc-dev-guide/src/hir/lowering.md b/src/doc/rustc-dev-guide/src/hir/lowering.md index b7d0293bede98..d555e0e7b3a85 100644 --- a/src/doc/rustc-dev-guide/src/hir/lowering.md +++ b/src/doc/rustc-dev-guide/src/hir/lowering.md @@ -2,8 +2,8 @@ The AST lowering step converts AST to [HIR](../hir.md). This means many structures are removed if they are irrelevant -for type analysis or similar syntax agnostic analyses. Examples -of such structures include but are not limited to +for type analysis or similar syntax-agnostic analyses. +Examples of such structures include but are not limited to * Parenthesis * Removed without replacement, the tree structure makes order explicit @@ -19,16 +19,19 @@ The implementation of AST lowering is in the [`rustc_ast_lowering`] crate. The entry point is [`lower_to_hir`], which retrieves the post-expansion AST and resolver data from [`TyCtxt`] and builds the [`hir::Crate`] for the whole crate. -Lowering is organized around HIR owners. [`lower_to_hir`] first indexes the +Lowering is organized around HIR owners. +[`lower_to_hir`] first indexes the crate and then [`ItemLowerer::lower_node`] lowers each crate, item, associated item, and foreign item. -Most of the lowering logic lives on [`LoweringContext`]. The implementation is +Most of the lowering logic lives on [`LoweringContext`]. +The implementation is split across multiple files in the [`rustc_ast_lowering`] crate such as `item.rs`, `expr.rs`, `pat.rs`, `path.rs`, and others, but they all share the same [`LoweringContext`] state and ID‑lowering machinery. -Each owner is lowered in its own [`with_hir_id_owner`] scope. This is why the +Each owner is lowered in its own [`with_hir_id_owner`] scope. +This is why the `HirId` invariants below matter: `lower_node_id` maps AST `NodeId`s into the current owner, while `next_id` creates fresh HIR-only nodes introduced during desugaring. @@ -36,25 +39,27 @@ desugaring. Lowering needs to uphold several invariants in order to not trigger the sanity checks in [`compiler/rustc_passes/src/hir_id_validator.rs`][hir_id_validator]: -1. A `HirId` must be used if created. So if you use the `lower_node_id`, - you *must* use the resulting `NodeId` or `HirId` (either is fine, since - any `NodeId`s in the `HIR` are checked for existing `HirId`s) +1. A `HirId` must be used if created. + So, if you use the `lower_node_id`, + you *must* use the resulting `NodeId` or `HirId` (either is fine, since + any `NodeId`s in the `HIR` are checked for existing `HirId`s). 2. Lowering a `HirId` must be done in the scope of the *owning* item. - This means you need to use `with_hir_id_owner` if you are creating parts - of an item other than the one being currently lowered. This happens for - example during the lowering of existential `impl Trait` + This means you need to use `with_hir_id_owner` if you are creating parts + of an item other than the one being currently lowered. + This happens, for example, during the lowering of existential `impl Trait`. 3. A `NodeId` that will be placed into a HIR structure must be lowered, - even if its `HirId` is unused. Calling - `let _ = self.lower_node_id(node_id);` is perfectly legitimate. + even if its `HirId` is unused. + Calling `let _ = self.lower_node_id(node_id);` is perfectly legitimate. 4. If you are creating new nodes that didn't exist in the `AST`, you *must* - create new ids for them. This is done by calling the `next_id` method, - which produces both a new `NodeId` as well as automatically lowering it - for you so you also get the `HirId`. + create new ids for them. + This is done by calling the `next_id` method, + which produces both a new `NodeId` as well as automatically lowering it + for you so you also get the `HirId`. [`rustc_ast_lowering`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/index.html [`lower_to_hir`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/fn.lower_to_hir.html [`TyCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html -[`hir::Crate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Crate.html +[`hir::Crate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/struct.Crate.html [`ItemLowerer::lower_node`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/item/struct.ItemLowerer.html [`LoweringContext`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/struct.LoweringContext.html [`with_hir_id_owner`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/struct.LoweringContext.html#method.with_hir_id_owner @@ -62,12 +67,16 @@ sanity checks in [`compiler/rustc_passes/src/hir_id_validator.rs`][hir_id_valida If you are creating new `DefId`s, since each `DefId` needs to have a corresponding `NodeId`, it is advisable to add these `NodeId`s to the -`AST` so you don't have to generate new ones during lowering. This has +`AST` so you don't have to generate new ones during lowering. +This has the advantage of creating a way to find the `DefId` of something via its -`NodeId`. If lowering needs this `DefId` in multiple places, you can't +`NodeId`. +If lowering needs this `DefId` in multiple places, you can't generate a new `NodeId` in all those places because you'd also get a new -`DefId` then. With a `NodeId` from the `AST` this is not an issue. +`DefId` then. +With a `NodeId` from the `AST`, this is not an issue. Having the `NodeId` also allows the `DefCollector` to generate the `DefId`s -instead of lowering having to do it on the fly. Centralizing the `DefId` +instead of lowering having to do it on the fly. +Centralizing the `DefId` generation in one place makes it easier to refactor and reason about. diff --git a/src/doc/rustc-dev-guide/src/implementing-new-features.md b/src/doc/rustc-dev-guide/src/implementing-new-features.md index bb5c0bbba6e3e..b288cb4d9ff91 100644 --- a/src/doc/rustc-dev-guide/src/implementing-new-features.md +++ b/src/doc/rustc-dev-guide/src/implementing-new-features.md @@ -162,7 +162,7 @@ The below steps needs to be followed in order to implement a new unstable featur ```rust ignore /// Allows defining identifiers beyond ASCII. - (unstable, non_ascii_idents, "CURRENT_RUSTC_VERSION", Some(55467), None), + (unstable, non_ascii_idents, "CURRENT_RUSTC_VERSION", Some(55467)), ``` Features can be marked as incomplete, @@ -173,7 +173,7 @@ The below steps needs to be followed in order to implement a new unstable featur ```rust ignore /// Allows deref patterns. - (incomplete, deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121), None), + (incomplete, deref_patterns, "CURRENT_RUSTC_VERSION", Some(87121)), ``` Feature flags related to a lang experiment must be marked as `incomplete` diff --git a/src/doc/rustc-dev-guide/src/name-resolution.md b/src/doc/rustc-dev-guide/src/name-resolution.md index 2e96382f77970..79d0897b4279e 100644 --- a/src/doc/rustc-dev-guide/src/name-resolution.md +++ b/src/doc/rustc-dev-guide/src/name-resolution.md @@ -1,36 +1,42 @@ # Name resolution In the previous chapters, we saw how the [*Abstract Syntax Tree* (`AST`)][ast] -is built with all macros expanded. We saw how doing that requires doing some -name resolution to resolve imports and macro names. In this chapter, we show -how this is actually done and more. +is built with all macros expanded. +We saw how doing that requires doing some +name resolution to resolve imports and macro names. +In this chapter, we show how this is actually done and more. [ast]: ./ast-validation.md In fact, we don't do full name resolution during macro expansion -- we only -resolve imports and macros at that time. This is required to know what to even -expand. Later, after we have the whole AST, we do full name resolution to -resolve all names in the crate. This happens in [`rustc_resolve::late`][late]. +resolve imports and macros at that time. +This is required to know what to even expand. +Later, after we have the whole AST, we do full name resolution to +resolve all names in the crate. +This happens in [`rustc_resolve::late`][late]. Unlike during macro expansion, in this late expansion, we only need to try to -resolve a name once, since no new names can be added. If we fail to resolve a -name, then it is a compiler error. +resolve a name once, since no new names can be added. +If we fail to resolve a name, then it is a compiler error. Name resolution is complex. There are different namespaces (e.g. macros, values, types, lifetimes), and names may be valid at different (nested) -scopes. Also, different types of names can fail resolution differently, and -failures can happen differently at different scopes. For example, in a module -scope, failure means no unexpanded macros and no unresolved glob imports in -that module. On the other hand, in a function body scope, failure requires that a -name be absent from the block we are in, all outer scopes, and the global -scope. +scopes. +Also, different types of names can fail resolution differently, and +failures can happen differently at different scopes. +For example, in a module scope, +failure means no unexpanded macros and no unresolved glob imports in +that module. +On the other hand, in a function body scope, failure requires that a +name be absent from the block we are in, all outer scopes, and the global scope. [late]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/index.html ## Basics In our programs we refer to variables, types, functions, etc, by giving them -a name. These names are not always unique. For example, take this valid Rust -program: +a name. +These names are not always unique. +For example, take this valid Rust program: ```rust type x = u32; @@ -38,20 +44,25 @@ let x: x = 1; let y: x = 2; ``` -How do we know on line 3 whether `x` is a type (`u32`) or a value (1)? These -conflicts are resolved during name resolution. In this specific case, name -resolution defines that type names and variable names live in separate +How do we know on line 3 whether `x` is a type (`u32`) or a value (1)? +These conflicts are resolved during name resolution. +In this specific case, +name resolution defines that type names and variable names live in separate namespaces and therefore can co-exist. -The name resolution in Rust is a two-phase process. In the first phase, which runs -during `macro` expansion, we build a tree of modules and resolve imports. Macro -expansion and name resolution communicate with each other via the +The name resolution in Rust is a two-phase process. +In the first phase, +which runs during `macro` expansion, +we build a tree of modules and resolve imports. +Macro expansion and name resolution communicate with each other via the [`ResolverAstLoweringExt`] trait. The input to the second phase is the syntax tree, produced by parsing input -files and expanding `macros`. This phase produces links from all the names in the -source to relevant places where the name was introduced. It also generates -helpful error messages, like typo suggestions, traits to import or lints about +files and expanding `macros`. +This phase produces links from all the names in the +source to relevant places where the name was introduced. +It also generates helpful error messages, +like typo suggestions, traits to import or lints about unused items. A successful run of the second phase ([`Resolver::resolve_crate`]) creates kind @@ -68,9 +79,11 @@ The name resolution lives in the [`rustc_resolve`] crate, with the bulk in ## Namespaces Different kind of symbols live in different namespaces ‒ e.g. types don't -clash with variables. This usually doesn't happen, because variables start with +clash with variables. +This usually doesn't happen, because variables start with lower-case letter while types with upper-case one, but this is only a -convention. This is legal Rust code that will compile (with warnings): +convention. +This is legal Rust code that will compile (with warnings): ```rust type x = u32; @@ -83,19 +96,21 @@ namespaces, the resolver keeps them separated and builds separate structures for them. In other words, when the code talks about namespaces, it doesn't mean the module -hierarchy, it's types vs. values vs. macros. +hierarchy, it's types versus values versus macros. ## Scopes and ribs -A name is visible only in certain area in the source code. This forms a -hierarchical structure, but not necessarily a simple one ‒ if one scope is +A name is visible only in certain area in the source code. +This forms a hierarchical structure, +but not necessarily a simple one ‒ if one scope is part of another, it doesn't mean a name visible in the outer scope is also visible in the inner scope, or that it refers to the same thing. -To cope with that, the compiler introduces the concept of [`Rib`]s. This is -an abstraction of a scope. Every time the set of visible names potentially changes, -a new [`Rib`] is pushed onto a stack. The places where this can happen include for -example: +To cope with that, the compiler introduces the concept of [`Rib`]s. +This is an abstraction of a scope. +Every time the set of visible names potentially changes, +a new [`Rib`] is pushed onto a stack. +The places where this can happen include for example: [`Rib`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.Rib.html @@ -106,11 +121,14 @@ example: * Macro expansion border ‒ to cope with macro hygiene. When searching for a name, the stack of [`ribs`] is traversed from the innermost -outwards. This helps to find the closest meaning of the name (the one not -shadowed by anything else). The transition to outer [`Rib`] may also affect +outwards. +This helps to find the closest meaning of the name (the one not +shadowed by anything else). +The transition to outer [`Rib`] may also affect what names are usable ‒ if there are nested functions (not closures), the inner one can't access parameters and local bindings of the outer one, -even though they should be visible by ordinary scoping rules. An example: +even though they should be visible by ordinary scoping rules. +An example: [`ribs`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.LateResolutionVisitor.html#structfield.ribs @@ -139,11 +157,13 @@ blocks), which isn't a full namespace in its own right. ## Overall strategy To perform the name resolution of the whole crate, the syntax tree is traversed -top-down and every encountered name is resolved. This works for most kinds of -names, because at the point of use of a name it is already introduced in the [`Rib`] +top-down and every encountered name is resolved. +This works for most kinds of names, +because at the point of use of a name it is already introduced in the [`Rib`] hierarchy. -There are some exceptions to this. Items are bit tricky, because they can be +There are some exceptions to this. +Items are bit tricky, because they can be used even before encountered ‒ therefore every block needs to be first scanned for items to fill in its [`Rib`]. @@ -156,14 +176,15 @@ Therefore, the resolution is performed in multiple stages. ## Speculative crate loading To give useful errors, rustc suggests importing paths into scope if they're -not found. How does it do this? It looks through every module of every crate -and looks for possible matches. This even includes crates that haven't yet -been loaded! +not found. +How does it do this? +It looks through every module of every crate and looks for possible matches. +This even includes crates that haven't yet been loaded! Eagerly loading crates to include import suggestions that haven't yet been loaded is called _speculative crate loading_, because any errors it encounters -shouldn't be reported: [`rustc_resolve`] decided to load them, not the user. The function -that does this is [`lookup_import_candidates`] and lives in +shouldn't be reported: [`rustc_resolve`] decided to load them, not the user. +The function that does this is [`lookup_import_candidates`] and lives in [`rustc_resolve::diagnostics`]. [`rustc_resolve`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/index.html @@ -176,8 +197,9 @@ the load is speculative. ## TODO: [#16](https://github.com/rust-lang/rustc-dev-guide/issues/16) -This is a result of the first pass of learning the code. It is definitely -incomplete and not detailed enough. It also might be inaccurate in places. +This is a result of the first pass of learning the code. +It is definitely incomplete and not detailed enough. +It also might be inaccurate in places. Still, it probably provides useful first guidepost to what happens in there. * What exactly does it link to and how is that published and consumed by diff --git a/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md b/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md index b46233e98b413..101c793525246 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md @@ -6,13 +6,13 @@ This notification group deals with linker related issues and their integration within the compiler. -The group also has an associated Zulip stream ([`#t-compiler/linker`]) +The group also has an associated Zulip stream ([`#t-compiler/gpgpu-backend`]) where people can go to ask questions and discuss GPU-related topics and issues. if you're interested in participating, feel free to sign up for this group! To do so, open a PR against the [rust-lang/team] repository and add your GitHub user to [this file][gpu-target-team]. -[`#t-compiler/linker`]: https://rust-lang.zulipchat.com/#narrow/channel/585172-t-compiler.2Flinker +[`#t-compiler/gpgpu-backend`]: https://rust-lang.zulipchat.com/#narrow/channel/422870-t-compiler.2Fgpgpu-backend [rust-lang/team]: https://github.com/rust-lang/team [gpu-target-team]: https://github.com/rust-lang/team/blob/main/teams/gpu-target.toml diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md b/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md index 2d409ad201b13..23e0f4e08c485 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md @@ -6,14 +6,14 @@ The `rustc_private` feature allows external crates to use compiler internals. -### Using `rustc-private` with Official Toolchains +### Using `rustc_private` with official toolchains When using the `rustc_private` feature with official Rust toolchains distributed via rustup, you need to install two additional components: 1. **`rustc-dev`**: Provides compiler libraries 2. **`llvm-tools`**: Provides LLVM libraries required for linking -#### Installation Steps +#### Installation steps Install both components using rustup: @@ -21,7 +21,7 @@ Install both components using rustup: rustup component add rustc-dev llvm-tools ``` -#### Common Error +#### Common error Without the `llvm-tools` component, you'll encounter linking errors like: @@ -40,7 +40,7 @@ For custom-built toolchains or environments not using rustup, additional configu - LLVM libraries must be available in your system's library search paths - The LLVM version must match the one used to build your Rust toolchain -#### Troubleshooting Steps +#### Troubleshooting steps 1. Verify LLVM is installed and accessible 2. Ensure that library paths are set: @@ -53,9 +53,10 @@ For custom-built toolchains or environments not using rustup, additional configu When developing out-of-tree projects that use `rustc_private` crates, you can configure `rust-analyzer` to recognize these crates. -#### Configuration Steps +#### Configuration steps + +1. Configure `rust-analyzer.rustc.source` to `"discover"` in your editor settings. -1. Configure `rust-analyzer.rustc.source` to `"discover"` in your editor settings. For VS Code, add to `rust_analyzer_settings.json`: ```json { @@ -69,9 +70,39 @@ When developing out-of-tree projects that use `rustc_private` crates, you can co rustc_private = true ``` -This configuration allows `rust-analyzer` to properly recognize and provide IDE support for `rustc_private` crates in out-of-tree projects. +This configuration allows `rust-analyzer` to properly recognize and provide IDE support for `rustc_private` crates in out-of-tree projects. + +### Getting nightly documentation for `rustc_private` + +#### Latest nightly + +For the latest nightly, you can install the `rustc-docs` component and open it directly in your browser: + +```sh +rustup component add rustc-docs +rustup doc --rustc-docs +``` + +> Note: The `rustc-docs` component is only available for recent nightly toolchains and may not be present for every nightly date. It was first introduced in [PR #75560](https://github.com/rust-lang/rust/pull/75560) (August 2020). + +#### Older nightlies + +If you depend on compiler internals from an older nightly, you may want to refer to the internal documentation from that particular nightly. +The only way to do this is to generate the documentation locally. +For example, to get documentation for `nightly-2025-11-08`: + +Get the Git commit hash for that nightly: + +```sh +rustup toolchain install nightly-2025-11-08 +rustc +nightly-2025-11-08 --version --verbose +``` + +The output will include a `commit-hash` line identifying the exact source revision. +Check out `rust-lang/rust` at that commit, then follow the steps in [compiler documentation](../building/compiler-documenting.md). + -### Additional Resources +### Additional resources - [GitHub Issue #137421] explains that `rustc_private` linker failures often occur because `llvm-tools` is not installed diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md index 93c59675d8936..f80dc48cd4c28 100644 --- a/src/doc/rustc-dev-guide/src/stability.md +++ b/src/doc/rustc-dev-guide/src/stability.md @@ -3,45 +3,51 @@ This section is about the stability attributes and schemes that allow stable APIs to use unstable APIs internally in the rustc standard library. -**NOTE**: this section is for *library* features, not *language* features. For instructions on -stabilizing a language feature see [Stabilizing Features](./stabilization-guide.md). +**NOTE**: this section is for *library* features, not *language* features. +For instructions on stabilizing a language feature, +see [Stabilizing Features](./stabilization-guide.md). ## unstable The `#[unstable(feature = "foo", issue = "1234", reason = "lorem ipsum")]` -attribute explicitly marks an item as unstable. Items that are marked as +attribute explicitly marks an item as unstable. +Items that are marked as "unstable" cannot be used without a corresponding `#![feature]` attribute on -the crate, even on a nightly compiler. This restriction only applies across -crate boundaries, unstable items may be used within the crate that defines -them. +the crate, even on a nightly compiler. +This restriction only applies across +crate boundaries, unstable items may be used within the crate that defines them. -The `issue` field specifies the associated GitHub [issue number]. This field is -required and all unstable features should have an associated tracking issue. In -rare cases where there is no sensible value `issue = "none"` is used. +The `issue` field specifies the associated GitHub [issue number]. +This field is required, +and all unstable features should have an associated tracking issue. +In rare cases where there is no sensible value, `issue = "none"` is used. The `unstable` attribute infects all sub-items, where the attribute doesn't -have to be reapplied. So if you apply this to a module, all items in the module -will be unstable. +have to be reapplied. +So, if you apply this to a module, all items in the module will be unstable. -If you rename a feature, you can add `old_name = "old_name"` to produce a +If you rename a feature, you can add `old_name = "old_name"` to produce a useful error message. You can make specific sub-items stable by using the `#[stable]` attribute on -them. The stability scheme works similarly to how `pub` works. You can have -public functions of nonpublic modules and you can have stable functions in -unstable modules or vice versa. +them. +The stability scheme works similarly to how `pub` works. +You can have public functions of non-public modules, +and you can have stable functions in unstable modules or vice versa. Previously, due to a [rustc bug], stable items inside unstable modules were available to stable code in that location. As of September 2024, items with [accidentally stabilized paths] are marked with the `#[rustc_allowed_through_unstable_modules]` attribute -to prevent code dependent on those paths from breaking. Do *not* add this attribute -to any more items unless that is needed to avoid breaking changes. +to prevent code dependent on those paths from breaking. +Do *not* add this attribute to any more items, +unless that is needed to avoid breaking changes. The `unstable` attribute may also have the `soft` value, which makes it a -future-incompatible deny-by-default lint instead of a hard error. This is used -by the `bench` attribute which was accidentally accepted in the past. This -prevents breaking dependencies by leveraging Cargo's lint capping. +future-incompatible deny-by-default lint instead of a hard error. +This is used +by the `bench` attribute which was accidentally accepted in the past. +This prevents breaking dependencies by leveraging Cargo's lint capping. [issue number]: https://github.com/rust-lang/rust/issues [rustc bug]: https://github.com/rust-lang/rust/issues/15702 @@ -49,22 +55,26 @@ prevents breaking dependencies by leveraging Cargo's lint capping. ## stable The `#[stable(feature = "foo", since = "1.420.69")]` attribute explicitly -marks an item as stabilized. Note that stable functions may use unstable things in their body. +marks an item as stabilized. +Note that stable functions may use unstable things in their body. ## rustc_const_unstable The `#[rustc_const_unstable(feature = "foo", issue = "1234", reason = "lorem -ipsum")]` has the same interface as the `unstable` attribute. It is used to mark -`const fn` as having their constness be unstable. This is only needed in rare cases: +ipsum")]` has the same interface as the `unstable` attribute. +It is used to mark `const fn` as having their constness be unstable. +This is only needed in rare cases: - If a `const fn` makes use of unstable language features or intrinsics. (The compiler will tell you to add the attribute if you run into this.) - If a `const fn` is `#[stable]` but not yet intended to be const-stable. - To change the feature gate that is required to call a const-unstable intrinsic. Const-stability differs from regular stability in that it is *recursive*: a -`#[rustc_const_unstable(...)]` function cannot even be indirectly called from stable code. This is +`#[rustc_const_unstable(...)]` function cannot even be indirectly called from stable code. +This is to avoid accidentally leaking unstable compiler implementation artifacts to stable code or locking -us into the accidental quirks of an incomplete implementation. See the rustc_const_stable_indirect +us into the accidental quirks of an incomplete implementation. +See the rustc_const_stable_indirect and rustc_allow_const_fn_unstable attributes below for how to fine-tune this check. ## rustc_const_stable @@ -75,7 +85,8 @@ a `const fn` as having its constness be `stable`. ## rustc_const_stable_indirect The `#[rustc_const_stable_indirect]` attribute can be added to a `#[rustc_const_unstable(...)]` -function to make it callable from `#[rustc_const_stable(...)]` functions. This indicates that the +function to make it callable from `#[rustc_const_stable(...)]` functions. +This indicates that the function is ready for stable in terms of its implementation (i.e., it doesn't use any unstable compiler features); the only reason it is not const-stable yet are API concerns. @@ -105,7 +116,8 @@ To stabilize a feature, follow these steps: 1. Ask a **@T-libs-api** member to start an FCP on the tracking issue and wait for the FCP to complete (with `disposition-merge`). 2. Change `#[unstable(...)]` to `#[stable(since = "CURRENT_RUSTC_VERSION")]`. -3. Remove `#![feature(...)]` from any test or doc-test for this API. If the feature is used in the +3. Remove `#![feature(...)]` from any test or doc-test for this API. + If the feature is used in the compiler or tools, remove it from there as well. 4. If this is a `const fn`, add `#[rustc_const_stable(since = "CURRENT_RUSTC_VERSION")]`. Alternatively, if this is not supposed to be const-stabilized yet, @@ -121,14 +133,15 @@ and the associated ## allow_internal_unstable -Macros and compiler desugarings expose their bodies to the call -site. To work around not being able to use unstable things in the standard +Macros and compiler desugarings expose their bodies to the call site. +To work around not being able to use unstable things in the standard library's macros, there's the `#[allow_internal_unstable(feature1, feature2)]` attribute that allows the given features to be used in stable macros. Note that if a macro is used in const context and generates a call to a `#[rustc_const_unstable(...)]` function, that will *still* be rejected even with -`allow_internal_unstable`. Add `#[rustc_const_stable_indirect]` to the function to ensure the macro +`allow_internal_unstable`. +Add `#[rustc_const_stable_indirect]` to the function to ensure the macro cannot accidentally bypass the recursive const stability checks. ## rustc_allow_const_fn_unstable @@ -138,14 +151,16 @@ indirectly. However, sometimes we do know that a feature will get stabilized, just not when, or there is a stable (but e.g. runtime-slow) workaround, so we could always fall back to some stable version if we -scrapped the unstable feature. In those cases, the `[rustc_allow_const_fn_unstable(feature1, +scrapped the unstable feature. +In those cases, the `[rustc_allow_const_fn_unstable(feature1, feature2)]` attribute can be used to allow some unstable features in the body of a stable (or indirectly stable) `const fn`. You also need to take care to uphold the `const fn` invariant that calling it at runtime and -compile-time needs to behave the same (see also [this blog post][blog]). This means that you +compile-time needs to behave the same (see also [this blog post][blog]). +This means that you may not create a `const fn` that e.g. transmutes a memory address to an integer, -because the addresses of things are nondeterministic and often unknown at +because the addresses of things are non-deterministic and often unknown at compile-time. **Always ping @rust-lang/wg-const-eval if you are adding more @@ -159,7 +174,8 @@ Any crate that uses the `stable` or `unstable` attributes must include the ## deprecated Deprecations in the standard library are nearly identical to deprecations in -user code. When `#[deprecated]` is used on an item, it must also have a `stable` +user code. +When `#[deprecated]` is used on an item, it must also have a `stable` or `unstable `attribute. `deprecated` has the following form: @@ -172,20 +188,26 @@ or `unstable `attribute. )] ``` -The `suggestion` field is optional. If given, it should be a string that can be -used as a machine-applicable suggestion to correct the warning. This is +The `suggestion` field is optional. +If given, it should be a string that can be +used as a machine-applicable suggestion to correct the warning. +This is typically used when the identifier is renamed, but no other significant changes -are necessary. When the `suggestion` field is used, you need to have +are necessary. +When the `suggestion` field is used, you need to have `#![feature(deprecated_suggestion)]` at the crate root. Another difference from user code is that the `since` field is actually checked -against the current version of `rustc`. If `since` is in a future version, then +against the current version of `rustc`. +If `since` is in a future version, then the `deprecated_in_future` lint is triggered which is default `allow`, but most of the standard library raises it to a warning with `#![warn(deprecated_in_future)]`. ## unstable_feature_bound -The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. In std/core, an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate. +The `#[unstable_feature_bound(foo)]` attribute can be used together with `#[unstable]` attribute to mark an `impl` of stable type and stable trait as unstable. +In std/core, an item annotated with `#[unstable_feature_bound(foo)]` can only be used by another item that is also annotated with `#[unstable_feature_bound(foo)]`. +Outside of std/core, using an item with `#[unstable_feature_bound(foo)]` requires the feature to be enabled with `#![feature(foo)]` attribute on the crate. Currently, the items that can be annotated with `#[unstable_feature_bound]` are: - `impl` @@ -193,7 +215,8 @@ Currently, the items that can be annotated with `#[unstable_feature_bound]` are: - trait ## renamed and removed features -Unstable features can get renamed and removed. If you rename a feature, you can add `old_name = "old_name"` to the `#[unstable]` attribute. +Unstable features can get renamed and removed. +If you rename a feature, you can add `old_name = "old_name"` to the `#[unstable]` attribute. If you remove a feature, the `#!unstable_removed(feature = "foo", reason = "brief description", link = "link", since = "1.90.0")` attribute should be used to produce a good error message for users of the removed feature. diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index b656d9495e0b5..8f06d69d8cffe 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -454,6 +454,23 @@ More information is available in the [toolstate documentation]. [rust-toolstate]: https://rust-lang-nursery.github.io/rust-toolstate [toolstate documentation]: https://forge.rust-lang.org/infra/toolstate.html +## Public CI dashboard + +To monitor the Rust CI, you can have a look at the [public dashboard] maintained by the infra team. + +These are some useful panels from the dashboard: + +- Pipeline duration: check how long the auto builds take to run. +- Top slowest jobs: check which jobs are taking the longest to run. +- Change in median job duration: check what jobs are slowest than before. + This is useful for detecting regressions. +- Top failed jobs: check which jobs are failing the most. + +To learn more about the dashboard, see the [Datadog CI docs]. + +[Datadog CI docs]: https://docs.datadoghq.com/continuous_integration/ +[public dashboard]: https://p.datadoghq.com/sb/3a172e20-e9e1-11ed-80e3-da7ad0900002-b5f7bb7e08b664a06b08527da85f7e30 + ## Determining the CI configuration If you want to determine which `bootstrap.toml` settings are used in CI for a diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index ef30869ac7620..4c534e9ae0eda 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -458,7 +458,8 @@ as they must be compilable by a stage 0 rustc that may be a beta or even stable By default, run-make tests print each subprocess command and its stdout/stderr. When running with `--no-capture` on `panic=abort` test suites (such as `cg_clif`), -this can flood the terminal. Omit `--verbose-run-make-subprocess-output` to +this can flood the terminal. +Omit `--verbose-run-make-subprocess-output` to suppress this output for passing tests — failing tests always print regardless: ```bash diff --git a/src/doc/unstable-book/src/language-features/diagnostic-on-unmatch-args.md b/src/doc/unstable-book/src/language-features/diagnostic-on-unmatch-args.md new file mode 100644 index 0000000000000..514ad1969bb0f --- /dev/null +++ b/src/doc/unstable-book/src/language-features/diagnostic-on-unmatch-args.md @@ -0,0 +1,53 @@ +# `diagnostic_on_unmatch_args` + +The tracking issue for this feature is: [#155642] + +[#155642]: https://github.com/rust-lang/rust/issues/155642 + +------------------------ + +The `diagnostic_on_unmatch_args` feature adds the +`#[diagnostic::on_unmatch_args(...)]` attribute for declarative macros. +It lets a macro definition customize diagnostics for matcher failures after all arms have been +tried, such as incomplete invocations or trailing extra arguments. + +This attribute currently applies to declarative macros such as `macro_rules!` and `pub macro`. +It is currently used for errors emitted by declarative macro matching itself; fragment parser +errors still use their existing diagnostics. + +```rust,compile_fail +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args( + message = "invalid arguments to {This} macro invocation", + label = "expected a type and value here", + note = "this macro expects a type and a value, like `pair!(u8, 0)`", + note = "see ", +)] +macro_rules! pair { + ($ty:ty, $value:expr) => {}; +} + +pair!(u8); +``` + +This emits output like: + +```text +error: invalid arguments to pair macro invocation + --> example.rs:13:9 + | +9 | macro_rules! pair { + | ----------------- when calling this macro +... +13 | pair!(u8); + | ^ expected a type and value here + | +note: while trying to match `,` + --> example.rs:10:12 + | +10 | ($ty:ty, $value:expr) => {}; + | ^ + = note: this macro expects a type and a value, like `pair!(u8, 0)` + = note: see +``` diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 8cb43e0028708..8de899ea0eef9 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -153,7 +153,7 @@ pub(crate) fn write_not_crate_specific( include_sources: bool, ) -> Result<(), Error> { write_rendered_cross_crate_info(crates, dst, opt, include_sources, resource_suffix)?; - write_static_files(dst, opt, style_files, css_file_extension, resource_suffix)?; + write_resources(dst, opt, style_files, css_file_extension, resource_suffix)?; Ok(()) } @@ -183,43 +183,45 @@ fn write_rendered_cross_crate_info( /// Writes the static files, the style files, and the css extensions. /// Have to be careful about these, because they write to the root out dir. -fn write_static_files( +fn write_resources( dst: &Path, opt: &RenderOptions, style_files: &[StylePath], css_file_extension: Option<&Path>, resource_suffix: &str, ) -> Result<(), Error> { - let static_dir = dst.join("static.files"); - try_err!(fs::create_dir_all(&static_dir), &static_dir); - - // Handle added third-party themes - for entry in style_files { - let theme = entry.basename()?; - let extension = - try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path); - - // Skip the official themes. They are written below as part of STATIC_FILES_LIST. - if matches!(theme.as_str(), "light" | "dark" | "ayu") { - continue; - } + if opt.emit.is_empty() || opt.emit.contains(&EmitType::HtmlNonStaticFiles) { + // Handle added third-party themes + for entry in style_files { + let theme = entry.basename()?; + let extension = + try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path); + + // Skip the official themes. They are written below as part of STATIC_FILES_LIST. + if matches!(theme.as_str(), "light" | "dark" | "ayu") { + continue; + } - let bytes = try_err!(fs::read(&entry.path), &entry.path); - let filename = format!("{theme}{resource_suffix}.{extension}"); - let dst_filename = dst.join(filename); - try_err!(fs::write(&dst_filename, bytes), &dst_filename); - } + let bytes = try_err!(fs::read(&entry.path), &entry.path); + let filename = format!("{theme}{resource_suffix}.{extension}"); + let dst_filename = dst.join(filename); + try_err!(fs::write(&dst_filename, bytes), &dst_filename); + } - // When the user adds their own CSS files with --extend-css, we write that as an - // invocation-specific file (that is, with a resource suffix). - if let Some(css) = css_file_extension { - let buffer = try_err!(fs::read_to_string(css), css); - let path = static_files::suffix_path("theme.css", resource_suffix); - let dst_path = dst.join(path); - try_err!(fs::write(&dst_path, buffer), &dst_path); + // When the user adds their own CSS files with --extend-css, we write that as an + // invocation-specific file (that is, with a resource suffix). + if let Some(css) = css_file_extension { + let buffer = try_err!(fs::read_to_string(css), css); + let path = static_files::suffix_path("theme.css", resource_suffix); + let dst_path = dst.join(path); + try_err!(fs::write(&dst_path, buffer), &dst_path); + } } if opt.emit.is_empty() || opt.emit.contains(&EmitType::HtmlStaticFiles) { + let static_dir = dst.join("static.files"); + try_err!(fs::create_dir_all(&static_dir), &static_dir); + static_files::for_each(|f: &static_files::StaticFile| { let filename = static_dir.join(f.output_filename()); let contents: &[u8] = diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 750ce27ea7962..87de4244b5c85 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -81,6 +81,7 @@ use rustc_span::{BytePos, Span, SyntaxContext}; use tracing::info; use crate::clean::utils::DOC_RUST_LANG_ORG_VERSION; +use crate::config::EmitType; use crate::error::Error; use crate::formats::cache::Cache; @@ -868,7 +869,11 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { }; rustc_interface::create_and_enter_global_ctxt(compiler, krate, |tcx| { let has_dep_info = render_options.dep_info().is_some(); - markdown::render_and_write(file, render_options, edition)?; + if render_options.emit.contains(&EmitType::HtmlNonStaticFiles) + || render_options.emit.is_empty() + { + markdown::render_and_write(file, render_options, edition)?; + } if has_dep_info { // Register the loaded external files in the source map so they show up in depinfo. // We can't load them via the source map because it gets created after we process the options. diff --git a/tests/run-make/rustdoc-dep-info/theme.css b/tests/run-make/rustdoc-dep-info/custom_theme.css similarity index 100% rename from tests/run-make/rustdoc-dep-info/theme.css rename to tests/run-make/rustdoc-dep-info/custom_theme.css diff --git a/tests/run-make/rustdoc-dep-info/rmake.rs b/tests/run-make/rustdoc-dep-info/rmake.rs index 233225fde8696..fbeac251f5f97 100644 --- a/tests/run-make/rustdoc-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-dep-info/rmake.rs @@ -9,8 +9,6 @@ use run_make_support::{path, rfs, rustdoc}; fn main() { rfs::create_dir("doc"); - // We're only emitting dep info, so we shouldn't be running static analysis to - // figure out that this program is erroneous. // Ensure that all kinds of input reading flags end up in dep-info. rustdoc() .input("lib.rs") @@ -18,7 +16,7 @@ fn main() { .arg("--html-before-content=before.html") .arg("--markdown-after-content=after.md") .arg("--extend-css=extend.css") - .arg("--theme=theme.css") + .arg("--theme=custom_theme.css") .arg("--index-page=index-page.md") .emit("dep-info") .run(); @@ -31,8 +29,31 @@ fn main() { assert_contains(&content, "after.md:"); assert_contains(&content, "before.html:"); assert_contains(&content, "extend.css:"); - assert_contains(&content, "theme.css:"); + assert_contains(&content, "custom_theme.css:"); assert_contains(&content, "index-page.md:"); + // Only emit dep-info. Don't emit the actual page. + assert!(!path("doc/foo/index.html").exists()); + assert!(!path("doc/custom_theme.css").exists()); + // weird that --extend-css generates a file named theme.css + assert!(!path("doc/theme.css").exists()); + + // Now try emitting dep-info and html files at the same time. + rustdoc() + .input("lib.rs") + .arg("-Zunstable-options") + .arg("--html-before-content=before.html") + .arg("--markdown-after-content=after.md") + .arg("--extend-css=extend.css") + .arg("--theme=custom_theme.css") + .arg("--index-page=index-page.md") + .emit("dep-info,html-non-static-files,html-static-files") + .run(); + assert!(path("doc/foo/index.html").exists()); + // These files are copied into the doc output folder, + // which is why they show up in dep-info. + assert!(path("doc/custom_theme.css").exists()); + // weird that --extend-css generates a file named theme.css + assert!(path("doc/theme.css").exists()); // Now we check that we can provide a file name to the `dep-info` argument. rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info=bla.d").run(); @@ -72,7 +93,9 @@ fn main() { assert_not_contains(&content, "after.md:"); assert_not_contains(&content, "before.html:"); assert_not_contains(&content, "extend.css:"); - assert_not_contains(&content, "theme.css:"); + assert_not_contains(&content, "custom_theme.css:"); + // Only emit dep-info, not the actual html. + assert!(!path("doc/example.html").exists()); // combine --emit=dep-info=filename with plain markdown input rustdoc() @@ -81,10 +104,12 @@ fn main() { .arg("--html-before-content=before.html") .arg("--markdown-after-content=after.md") .arg("--extend-css=extend.css") - .arg("--theme=theme.css") + .arg("--theme=custom_theme.css") + .arg("--markdown-css=markdown.css") .arg("--index-page=index-page.md") - .emit("dep-info=example.d") + .emit("dep-info=example.d,html-non-static-files,html-static-files") .run(); + assert!(path("doc/example.html").exists()); let content = rfs::read_to_string("example.d"); assert_contains(&content, "example.md:"); assert_not_contains(&content, "lib.rs:"); @@ -93,7 +118,17 @@ fn main() { assert_not_contains(&content, "doc.md:"); assert_contains(&content, "after.md:"); assert_contains(&content, "before.html:"); - assert_contains(&content, "extend.css:"); - assert_contains(&content, "theme.css:"); assert_contains(&content, "index-page.md:"); + // This is a hotlink, not a file that gets copied, + // so it shouldn't add to the dep-info, it shouldn't be copied, + // and it shouldn't be resolved relative to the root path. + // + // It's weird that this is different from the other two css + // files, but it's stable, so I can't change it. + assert!(!path("doc/markdown.css").exists()); + assert_not_contains(&content, "markdown.css:"); + // These files aren't actually used, and the fact that they show up + // is arguably a bug, but test it anyway. + assert_contains(&content, "extend.css:"); + assert_contains(&content, "custom_theme.css:"); } diff --git a/tests/ui-fulldeps/rustc_public/check_fn_attrs.rs b/tests/ui-fulldeps/rustc_public/check_fn_attrs.rs new file mode 100644 index 0000000000000..552e8cbec376d --- /dev/null +++ b/tests/ui-fulldeps/rustc_public/check_fn_attrs.rs @@ -0,0 +1,93 @@ +//@ run-pass +//! Test that users are able to query function-level constness and asyncness. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ edition: 2021 + +#![feature(rustc_private)] + +extern crate rustc_driver; +extern crate rustc_interface; +extern crate rustc_middle; +#[macro_use] +extern crate rustc_public; + +use rustc_public::crate_def::CrateDef; +use rustc_public::ty::{Asyncness, Constness, FnDef}; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "input"; + +fn test_stable_mir() -> ControlFlow<()> { + let fns = rustc_public::local_crate().fn_defs(); + + check_fn(&fns, "input::const_sync", Constness::Const, Asyncness::NotAsync); + check_fn(&fns, "input::async_fn", Constness::NotConst, Asyncness::Async); + check_fn(&fns, "input::plain", Constness::NotConst, Asyncness::NotAsync); + check_fn(&fns, "input::Widget::assoc_const", Constness::Const, Asyncness::NotAsync); + check_fn(&fns, "input::Widget::assoc_async", Constness::NotConst, Asyncness::Async); + check_fn(&fns, "input::Widget::assoc_plain", Constness::NotConst, Asyncness::NotAsync); + + ControlFlow::Continue(()) +} + +fn check_fn(fns: &[FnDef], name: &str, constness: Constness, asyncness: Asyncness) { + let fn_def = + fns.iter().find(|def| def.name() == name).unwrap_or_else(|| panic!("missing {name}")); + assert_eq!(fn_def.constness(), constness, "wrong constness for {}", fn_def.name()); + assert_eq!(fn_def.asyncness(), asyncness, "wrong asyncness for {}", fn_def.name()); +} + +fn main() { + let path = "fn_attrs_input.rs"; + generate_input(&path).unwrap(); + let args = &[ + "rustc".to_string(), + "--edition=2021".to_string(), + "--crate-type=lib".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_stable_mir).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + pub const fn const_sync() -> u32 {{ + 1 + }} + + pub async fn async_fn() -> u32 {{ + 2 + }} + + pub fn plain() -> u32 {{ + 3 + }} + + pub struct Widget; + + impl Widget {{ + pub const fn assoc_const() -> u32 {{ + 4 + }} + + pub async fn assoc_async() -> u32 {{ + 5 + }} + + pub fn assoc_plain() -> u32 {{ + 6 + }} + }} + "# + )?; + Ok(()) +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/auxiliary/other.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/auxiliary/other.rs new file mode 100644 index 0000000000000..2dd032ecf41cb --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/auxiliary/other.rs @@ -0,0 +1,12 @@ +#![feature(diagnostic_on_unmatch_args)] + +#[macro_export] +#[diagnostic::on_unmatch_args( + message = "invalid arguments to {This} macro invocation", + label = "expected a type and value here", + note = "this macro expects a type and a value, like `pair!(u8, 0)`", + note = "see the macro documentation for accepted forms", +)] +macro_rules! pair { + ($ty:ty, $value:expr) => {}; +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/error_is_shown_in_downstream_crates.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/error_is_shown_in_downstream_crates.rs new file mode 100644 index 0000000000000..0d5f17f5c5c2e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/error_is_shown_in_downstream_crates.rs @@ -0,0 +1,12 @@ +//@ aux-build:other.rs + +extern crate other; + +fn main() { + other::pair!(u8); + //~^ ERROR invalid arguments to pair macro invocation + //~| NOTE expected a type and value here + //~| NOTE while trying to match `,` + //~| NOTE this macro expects a type and a value, like `pair!(u8, 0)` + //~| NOTE see the macro documentation for accepted forms +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/error_is_shown_in_downstream_crates.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/error_is_shown_in_downstream_crates.stderr new file mode 100644 index 0000000000000..c095958350464 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/error_is_shown_in_downstream_crates.stderr @@ -0,0 +1,16 @@ +error: invalid arguments to pair macro invocation + --> $DIR/error_is_shown_in_downstream_crates.rs:6:20 + | +LL | other::pair!(u8); + | ^ expected a type and value here + | +note: while trying to match `,` + --> $DIR/auxiliary/other.rs:11:12 + | +LL | ($ty:ty, $value:expr) => {}; + | ^ + = note: this macro expects a type and a value, like `pair!(u8, 0)` + = note: see the macro documentation for accepted forms + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/message_and_label.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/message_and_label.rs new file mode 100644 index 0000000000000..26a8b07bd6e48 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/message_and_label.rs @@ -0,0 +1,21 @@ +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args( + message = "invalid arguments to {This} macro invocation", + label = "expected a type and value here", + note = "this macro expects a type and a value, like `pair!(u8, 0)`", + note = "see the macro documentation for accepted forms", +)] +macro_rules! pair { + //~^ NOTE when calling this macro + ($ty:ty, $value:expr) => {}; + //~^ NOTE while trying to match `,` +} + +fn main() { + pair!(u8); + //~^ ERROR invalid arguments to pair macro invocation + //~| NOTE expected a type and value here + //~| NOTE this macro expects a type and a value, like `pair!(u8, 0)` + //~| NOTE see the macro documentation for accepted forms +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/message_and_label.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/message_and_label.stderr new file mode 100644 index 0000000000000..704479a1b1fa3 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/message_and_label.stderr @@ -0,0 +1,19 @@ +error: invalid arguments to pair macro invocation + --> $DIR/message_and_label.rs:16:13 + | +LL | macro_rules! pair { + | ----------------- when calling this macro +... +LL | pair!(u8); + | ^ expected a type and value here + | +note: while trying to match `,` + --> $DIR/message_and_label.rs:11:12 + | +LL | ($ty:ty, $value:expr) => {}; + | ^ + = note: this macro expects a type and a value, like `pair!(u8, 0)` + = note: see the macro documentation for accepted forms + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/notes_on_extra_args.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/notes_on_extra_args.rs new file mode 100644 index 0000000000000..083445c7f9691 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/notes_on_extra_args.rs @@ -0,0 +1,21 @@ +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args( + message = "{This}! expects exactly two arguments", + label = "unexpected extra input starts here", + note = "this macro expects a type and a value, like `pair!(u8, 0)`", + note = "make sure to pass both arguments", +)] +macro_rules! pair { + //~^ NOTE when calling this macro + ($ty:ty, $value:expr) => {}; + //~^ NOTE while trying to match meta-variable `$value:expr` +} + +fn main() { + pair!(u8, 0, 42); + //~^ ERROR pair! expects exactly two arguments + //~| NOTE unexpected extra input starts here + //~| NOTE this macro expects a type and a value, like `pair!(u8, 0)` + //~| NOTE make sure to pass both arguments +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/notes_on_extra_args.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/notes_on_extra_args.stderr new file mode 100644 index 0000000000000..e10b8ef46bac0 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/notes_on_extra_args.stderr @@ -0,0 +1,19 @@ +error: pair! expects exactly two arguments + --> $DIR/notes_on_extra_args.rs:16:16 + | +LL | macro_rules! pair { + | ----------------- when calling this macro +... +LL | pair!(u8, 0, 42); + | ^ unexpected extra input starts here + | +note: while trying to match meta-variable `$value:expr` + --> $DIR/notes_on_extra_args.rs:11:14 + | +LL | ($ty:ty, $value:expr) => {}; + | ^^^^^^^^^^^ + = note: this macro expects a type and a value, like `pair!(u8, 0)` + = note: make sure to pass both arguments + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/on_unmatch_args.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/on_unmatch_args.rs new file mode 100644 index 0000000000000..a4fc1460b60ed --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/on_unmatch_args.rs @@ -0,0 +1,19 @@ +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args( + note = "this macro expects a type and a value, like `pair!(u8, 0)`", + note = "make sure to pass both arguments", +)] +macro_rules! pair { + //~^ NOTE when calling this macro + ($ty:ty, $value:expr) => {}; + //~^ NOTE while trying to match `,` +} + +fn main() { + pair!(u8); + //~^ ERROR unexpected end of macro invocation + //~| NOTE missing tokens in macro arguments + //~| NOTE this macro expects a type and a value, like `pair!(u8, 0)` + //~| NOTE make sure to pass both arguments +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/on_unmatch_args.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/on_unmatch_args.stderr new file mode 100644 index 0000000000000..9d3c4a5392cde --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/on_unmatch_args.stderr @@ -0,0 +1,19 @@ +error: unexpected end of macro invocation + --> $DIR/on_unmatch_args.rs:14:13 + | +LL | macro_rules! pair { + | ----------------- when calling this macro +... +LL | pair!(u8); + | ^ missing tokens in macro arguments + | +note: while trying to match `,` + --> $DIR/on_unmatch_args.rs:9:12 + | +LL | ($ty:ty, $value:expr) => {}; + | ^ + = note: this macro expects a type and a value, like `pair!(u8, 0)` + = note: make sure to pass both arguments + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/other_match_macro_error.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/other_match_macro_error.rs new file mode 100644 index 0000000000000..36197997b3e60 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/other_match_macro_error.rs @@ -0,0 +1,14 @@ +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args( + message = "invalid route method", + note = "this macro expects a action, like `{This}!(get \"/hello\")`" +)] +macro_rules! route { + (get $path:literal) => {}; +} + +fn main() { + route!(post "/"); + //~^ ERROR invalid route method +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/other_match_macro_error.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/other_match_macro_error.stderr new file mode 100644 index 0000000000000..5f6c59e79287d --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/other_match_macro_error.stderr @@ -0,0 +1,18 @@ +error: invalid route method + --> $DIR/other_match_macro_error.rs:12:12 + | +LL | macro_rules! route { + | ------------------ when calling this macro +... +LL | route!(post "/"); + | ^^^^ no rules expected this token in macro call + | +note: while trying to match `get` + --> $DIR/other_match_macro_error.rs:8:6 + | +LL | (get $path:literal) => {}; + | ^^^ + = note: this macro expects a action, like `route!(get "/hello")` + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_formats.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_formats.rs new file mode 100644 index 0000000000000..65cf18168638e --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_formats.rs @@ -0,0 +1,14 @@ +//@ check-pass +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args( + message = "{T}! is missing arguments", + //~^ WARN unknown parameter `T` +)] +macro_rules! pair { + ($ty:ty, $value:expr) => {}; +} + +fn main() { + pair!(u8, 0); +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_formats.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_formats.stderr new file mode 100644 index 0000000000000..2f631337c4416 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_formats.stderr @@ -0,0 +1,11 @@ +warning: unknown parameter `T` + --> $DIR/report_warning_on_invalid_formats.rs:5:17 + | +LL | message = "{T}! is missing arguments", + | ^ + | + = help: use `{This}` to refer to the macro name + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_meta_item_syntax.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_meta_item_syntax.rs new file mode 100644 index 0000000000000..aa5371de07c09 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_meta_item_syntax.rs @@ -0,0 +1,12 @@ +//@ check-pass +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args = "foo"] +//~^ WARN malformed `diagnostic::on_unmatch_args` attribute [malformed_diagnostic_attributes] +macro_rules! pair { + ($ty:ty, $value:expr) => {}; +} + +fn main() { + pair!(u8, 0); +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_meta_item_syntax.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_meta_item_syntax.stderr new file mode 100644 index 0000000000000..51f25f1165d60 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_invalid_meta_item_syntax.stderr @@ -0,0 +1,11 @@ +warning: malformed `diagnostic::on_unmatch_args` attribute + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:4:1 + | +LL | #[diagnostic::on_unmatch_args = "foo"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_missing_options.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_missing_options.rs new file mode 100644 index 0000000000000..13eec1834d7cd --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_missing_options.rs @@ -0,0 +1,12 @@ +//@ check-pass +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args] +//~^ WARN missing options for `diagnostic::on_unmatch_args` attribute [malformed_diagnostic_attributes] +macro_rules! pair { + ($ty:ty, $value:expr) => {}; +} + +fn main() { + pair!(u8, 0); +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_missing_options.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_missing_options.stderr new file mode 100644 index 0000000000000..bbd8dba4e6206 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_missing_options.stderr @@ -0,0 +1,11 @@ +warning: missing options for `diagnostic::on_unmatch_args` attribute + --> $DIR/report_warning_on_missing_options.rs:4:1 + | +LL | #[diagnostic::on_unmatch_args] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_non_macro.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_non_macro.rs new file mode 100644 index 0000000000000..6f8629d679a0c --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_non_macro.rs @@ -0,0 +1,10 @@ +//@ check-pass +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args(message = "not allowed here")] +//~^ WARN `#[diagnostic::on_unmatch_args]` can only be applied to macro definitions +struct Foo; + +fn main() { + let _ = Foo; +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_non_macro.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_non_macro.stderr new file mode 100644 index 0000000000000..c6d1b620c44be --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_non_macro.stderr @@ -0,0 +1,10 @@ +warning: `#[diagnostic::on_unmatch_args]` can only be applied to macro definitions + --> $DIR/report_warning_on_non_macro.rs:4:1 + | +LL | #[diagnostic::on_unmatch_args(message = "not allowed here")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_unknown_options.rs b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_unknown_options.rs new file mode 100644 index 0000000000000..b1e50256dc886 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_unknown_options.rs @@ -0,0 +1,12 @@ +//@ check-pass +#![feature(diagnostic_on_unmatch_args)] + +#[diagnostic::on_unmatch_args(unsupported = "foo")] +//~^ WARN malformed `diagnostic::on_unmatch_args` attribute [malformed_diagnostic_attributes] +macro_rules! pair { + ($ty:ty, $value:expr) => {}; +} + +fn main() { + pair!(u8, 0); +} diff --git a/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_unknown_options.stderr b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_unknown_options.stderr new file mode 100644 index 0000000000000..2e897a6b180da --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_unmatch_args/report_warning_on_unknown_options.stderr @@ -0,0 +1,11 @@ +warning: malformed `diagnostic::on_unmatch_args` attribute + --> $DIR/report_warning_on_unknown_options.rs:4:31 + | +LL | #[diagnostic::on_unmatch_args(unsupported = "foo")] + | ^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: 1 warning emitted + diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unmatch-args.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-unmatch-args.rs new file mode 100644 index 0000000000000..72686d1003279 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unmatch-args.rs @@ -0,0 +1,15 @@ +//! This is an unusual feature gate test, as it doesn't test the feature +//! gate, but the fact that not adding the feature gate will cause the +//! diagnostic to not emit the custom diagnostic message. +#[diagnostic::on_unmatch_args(note = "custom note")] +macro_rules! pair { + //~^ NOTE when calling this macro + ($ty:ty, $value:expr) => {}; + //~^ NOTE while trying to match `,` +} + +fn main() { + pair!(u8); + //~^ ERROR unexpected end of macro invocation + //~| NOTE missing tokens in macro arguments +} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-unmatch-args.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-unmatch-args.stderr new file mode 100644 index 0000000000000..39eecc8322b22 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-unmatch-args.stderr @@ -0,0 +1,17 @@ +error: unexpected end of macro invocation + --> $DIR/feature-gate-diagnostic-on-unmatch-args.rs:12:13 + | +LL | macro_rules! pair { + | ----------------- when calling this macro +... +LL | pair!(u8); + | ^ missing tokens in macro arguments + | +note: while trying to match `,` + --> $DIR/feature-gate-diagnostic-on-unmatch-args.rs:7:12 + | +LL | ($ty:ty, $value:expr) => {}; + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/field_representing_types/invalid.next.stderr b/tests/ui/field_representing_types/invalid.next.stderr index 2a5e884a5e3f9..611077a570229 100644 --- a/tests/ui/field_representing_types/invalid.next.stderr +++ b/tests/ui/field_representing_types/invalid.next.stderr @@ -6,6 +6,7 @@ LL | let _: field_of!(Struct); | note: while trying to match `,` --> $SRC_DIR/core/src/field.rs:LL:COL + = note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)` error: unexpected end of macro invocation --> $DIR/invalid.rs:24:29 @@ -15,6 +16,7 @@ LL | let _: field_of!(Struct,); | note: while trying to match meta-variable `$fields:expr` --> $SRC_DIR/core/src/field.rs:LL:COL + = note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)` error: no rules expected `extra` --> $DIR/invalid.rs:25:37 @@ -23,6 +25,7 @@ LL | let _: field_of!(Struct, field, extra); | ^^^^^ no rules expected this token in macro call | = note: while trying to match sequence end + = note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)` error: offset_of expects dot-separated field and variant names --> $DIR/invalid.rs:27:28 diff --git a/tests/ui/field_representing_types/invalid.old.stderr b/tests/ui/field_representing_types/invalid.old.stderr index 2a5e884a5e3f9..611077a570229 100644 --- a/tests/ui/field_representing_types/invalid.old.stderr +++ b/tests/ui/field_representing_types/invalid.old.stderr @@ -6,6 +6,7 @@ LL | let _: field_of!(Struct); | note: while trying to match `,` --> $SRC_DIR/core/src/field.rs:LL:COL + = note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)` error: unexpected end of macro invocation --> $DIR/invalid.rs:24:29 @@ -15,6 +16,7 @@ LL | let _: field_of!(Struct,); | note: while trying to match meta-variable `$fields:expr` --> $SRC_DIR/core/src/field.rs:LL:COL + = note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)` error: no rules expected `extra` --> $DIR/invalid.rs:25:37 @@ -23,6 +25,7 @@ LL | let _: field_of!(Struct, field, extra); | ^^^^^ no rules expected this token in macro call | = note: while trying to match sequence end + = note: this macro expects a container type and a field path, like `field_of!(Type, field)` or `field_of!(Enum, Variant.field)` error: offset_of expects dot-separated field and variant names --> $DIR/invalid.rs:27:28 diff --git a/tests/ui/offset-of/offset-of-arg-count.stderr b/tests/ui/offset-of/offset-of-arg-count.stderr index 0772bb18e0c62..ca6db9ecf0973 100644 --- a/tests/ui/offset-of/offset-of-arg-count.stderr +++ b/tests/ui/offset-of/offset-of-arg-count.stderr @@ -6,6 +6,7 @@ LL | offset_of!(NotEnoughArguments); | note: while trying to match `,` --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + = note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)` error: unexpected end of macro invocation --> $DIR/offset-of-arg-count.rs:5:45 @@ -15,6 +16,7 @@ LL | offset_of!(NotEnoughArgumentsWithAComma, ); | note: while trying to match meta-variable `$fields:expr` --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + = note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)` error: no rules expected `too` --> $DIR/offset-of-arg-count.rs:6:34 @@ -23,6 +25,7 @@ LL | offset_of!(Container, field, too many arguments); | ^^^ no rules expected this token in macro call | = note: while trying to match sequence end + = note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)` error: unexpected token: `)` --> $DIR/offset-of-arg-count.rs:9:21 diff --git a/tests/ui/offset-of/offset-of-tuple.stderr b/tests/ui/offset-of/offset-of-tuple.stderr index 33dea9918cac2..f90f2db1c6c35 100644 --- a/tests/ui/offset-of/offset-of-tuple.stderr +++ b/tests/ui/offset-of/offset-of-tuple.stderr @@ -72,6 +72,7 @@ LL | offset_of!((u8, u8), +1); | note: while trying to match meta-variable `$fields:expr` --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + = note: this macro expects a container type and a (nested) field path, like `offset_of!(Type, field)` error: offset_of expects dot-separated field and variant names --> $DIR/offset-of-tuple.rs:7:26 diff --git a/tests/ui/parser/let-chains-assign-add-incorrect.fixed b/tests/ui/parser/let-chains-assign-add-incorrect.fixed index b1abce8bd2df5..469c78bb6939c 100644 --- a/tests/ui/parser/let-chains-assign-add-incorrect.fixed +++ b/tests/ui/parser/let-chains-assign-add-incorrect.fixed @@ -15,6 +15,8 @@ fn test_where_left_is_not_let() { //~| ERROR binary assignment operation `+=` cannot be used in a let chain //~| NOTE cannot use `+=` in a let chain //~| HELP you might have meant to compare with `==` instead of assigning with `+=` + //~| ERROR mismatched types + //~| NOTE expected `bool`, found `()` } fn test_where_left_is_let() { @@ -28,6 +30,8 @@ fn test_where_left_is_let() { //~| ERROR binary assignment operation `+=` cannot be used in a let chain //~| NOTE cannot use `+=` in a let chain //~| HELP you might have meant to compare with `==` instead of assigning with `+=` + //~| ERROR mismatched types + //~| NOTE expected `bool`, found `()` } fn main() { diff --git a/tests/ui/parser/let-chains-assign-add-incorrect.rs b/tests/ui/parser/let-chains-assign-add-incorrect.rs index 3b2e5e93d91ac..6bdb4b648ebad 100644 --- a/tests/ui/parser/let-chains-assign-add-incorrect.rs +++ b/tests/ui/parser/let-chains-assign-add-incorrect.rs @@ -15,6 +15,8 @@ fn test_where_left_is_not_let() { //~| ERROR binary assignment operation `+=` cannot be used in a let chain //~| NOTE cannot use `+=` in a let chain //~| HELP you might have meant to compare with `==` instead of assigning with `+=` + //~| ERROR mismatched types + //~| NOTE expected `bool`, found `()` } fn test_where_left_is_let() { @@ -28,6 +30,8 @@ fn test_where_left_is_let() { //~| ERROR binary assignment operation `+=` cannot be used in a let chain //~| NOTE cannot use `+=` in a let chain //~| HELP you might have meant to compare with `==` instead of assigning with `+=` + //~| ERROR mismatched types + //~| NOTE expected `bool`, found `()` } fn main() { diff --git a/tests/ui/parser/let-chains-assign-add-incorrect.stderr b/tests/ui/parser/let-chains-assign-add-incorrect.stderr index dea8e6a1982b1..02926965edd0e 100644 --- a/tests/ui/parser/let-chains-assign-add-incorrect.stderr +++ b/tests/ui/parser/let-chains-assign-add-incorrect.stderr @@ -7,7 +7,7 @@ LL | if let _ = 1 && true && y += 2 {}; = note: only supported directly in conditions of `if` and `while` expressions error: expected expression, found `let` statement - --> $DIR/let-chains-assign-add-incorrect.rs:22:8 + --> $DIR/let-chains-assign-add-incorrect.rs:24:8 | LL | if let _ = 1 && y += 2 {}; | ^^^^^^^^^ @@ -37,13 +37,19 @@ LL + if let _ = 1 && true && y == 2 {}; | error[E0308]: mismatched types - --> $DIR/let-chains-assign-add-incorrect.rs:22:21 + --> $DIR/let-chains-assign-add-incorrect.rs:8:8 + | +LL | if let _ = 1 && true && y += 2 {}; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + +error[E0308]: mismatched types + --> $DIR/let-chains-assign-add-incorrect.rs:24:21 | LL | if let _ = 1 && y += 2 {}; | ^ expected `bool`, found integer error: binary assignment operation `+=` cannot be used in a let chain - --> $DIR/let-chains-assign-add-incorrect.rs:22:23 + --> $DIR/let-chains-assign-add-incorrect.rs:24:23 | LL | if let _ = 1 && y += 2 {}; | -------------- ^^ cannot use `+=` in a let chain @@ -56,6 +62,12 @@ LL - if let _ = 1 && y += 2 {}; LL + if let _ = 1 && y == 2 {}; | -error: aborting due to 6 previous errors +error[E0308]: mismatched types + --> $DIR/let-chains-assign-add-incorrect.rs:24:8 + | +LL | if let _ = 1 && y += 2 {}; + | ^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0308`.