From 6c6cccdd9b7f9c4a4fee19cf7881fbef081eda71 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 14 Jul 2022 19:19:15 -0400 Subject: [PATCH 01/30] interpret/validity: improve some comments --- compiler/rustc_const_eval/src/interpret/validity.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 53bc2cc8a69..2e5492ecf56 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -853,7 +853,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> self.visit_scalar(scalar, scalar_layout)?; } Abi::ScalarPair(a_layout, b_layout) => { - // We would validate these things as we descend into the fields, + // There is no `rustc_layout_scalar_valid_range_start` for pairs, so + // we would validate these things as we descend into the fields, // but that can miss bugs in layout computation. Layout computation // is subtle due to enums having ScalarPair layout, where one field // is the discriminant. @@ -867,7 +868,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> } Abi::Vector { .. } => { // No checks here, we assume layout computation gets this right. - // (This is harder to check since Miri does not represent these as `Immediate`.) + // (This is harder to check since Miri does not represent these as `Immediate`. We + // also cannot use field projections since this might be a newtype around a vector.) } Abi::Aggregate { .. } => { // Nothing to do. From 06f480661f66e35869521ccd60278107233f9670 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 16:28:38 +0100 Subject: [PATCH 02/30] errors: impl `IntoDiagnosticArg` for `char` Implements `IntoDiagnosticArg` for `char` using its `Debug` implementation and introduces a macro for those types which just delegate the implementation to `ToString`. Signed-off-by: David Wood --- compiler/rustc_errors/src/diagnostic.rs | 110 +++++++----------------- 1 file changed, 33 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index da321c45875..0ce7f3c7e82 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -40,6 +40,34 @@ pub trait IntoDiagnosticArg { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>; } +macro_rules! into_diagnostic_arg_using_display { + ($( $ty:ty ),+ $(,)?) => { + $( + impl IntoDiagnosticArg for $ty { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } + } + )+ + } +} + +into_diagnostic_arg_using_display!( + i8, + u8, + i16, + u16, + i32, + u32, + i64, + u64, + i128, + u128, + std::num::NonZeroU32, + Edition, + Ident, +); + impl IntoDiagnosticArg for bool { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { if self { @@ -50,81 +78,9 @@ impl IntoDiagnosticArg for bool { } } -impl IntoDiagnosticArg for i8 { +impl IntoDiagnosticArg for char { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u8 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i16 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u16 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i32 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u32 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i64 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u64 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for i128 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for u128 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for String { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self)) - } -} - -impl IntoDiagnosticArg for std::num::NonZeroU32 { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) - } -} - -impl IntoDiagnosticArg for Edition { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Owned(self.to_string())) + DiagnosticArgValue::Str(Cow::Owned(format!("{:?}", self))) } } @@ -134,15 +90,15 @@ impl IntoDiagnosticArg for Symbol { } } -impl IntoDiagnosticArg for Ident { +impl<'a> IntoDiagnosticArg for &'a str { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { self.to_string().into_diagnostic_arg() } } -impl<'a> IntoDiagnosticArg for &'a str { +impl IntoDiagnosticArg for String { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - self.to_string().into_diagnostic_arg() + DiagnosticArgValue::Str(Cow::Owned(self)) } } From c3fdf748856fa220dc251647808db8535ac0bba2 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 16:29:24 +0100 Subject: [PATCH 03/30] errors: lint on `LintDiagnosticBuilder::build` Apply the `#[rustc_lint_diagnostics]` attribute to `LintDiagnosticBuilder::build` so that diagnostic migration lints will trigger for it. Signed-off-by: David Wood --- .../locales/en-US/privacy.ftl | 9 +++++ .../rustc_errors/src/diagnostic_builder.rs | 1 + compiler/rustc_privacy/src/errors.rs | 18 +++++++++- compiler/rustc_privacy/src/lib.rs | 35 +++++++------------ 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/privacy.ftl b/compiler/rustc_error_messages/locales/en-US/privacy.ftl index 2b0778f48ca..f8a750da93f 100644 --- a/compiler/rustc_error_messages/locales/en-US/privacy.ftl +++ b/compiler/rustc_error_messages/locales/en-US/privacy.ftl @@ -10,3 +10,12 @@ privacy-unnamed-item-is-private = {$kind} is private privacy-in-public-interface = {$vis_descr} {$kind} `{$descr}` in public interface .label = can't leak {$vis_descr} {$kind} .visibility-label = `{$descr}` declared as {$vis_descr} + +privacy-from-private-dep-in-public-interface = + {$kind} `{$descr}` from private dependency '{$krate}' in public interface + +private-in-public-lint = + {$vis_descr} {$kind} `{$descr}` in public interface (error {$kind -> + [trait] E0445 + *[other] E0446 + }) diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 99ac6a3546e..9e68ee282e6 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -595,6 +595,7 @@ macro_rules! error_code { pub struct LintDiagnosticBuilder<'a, G: EmissionGuarantee>(DiagnosticBuilder<'a, G>); impl<'a, G: EmissionGuarantee> LintDiagnosticBuilder<'a, G> { + #[rustc_lint_diagnostics] /// Return the inner `DiagnosticBuilder`, first setting the primary message to `msg`. pub fn build(mut self, msg: impl Into) -> DiagnosticBuilder<'a, G> { self.0.set_primary_message(msg); diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs index 482721d373a..b0fac91f6eb 100644 --- a/compiler/rustc_privacy/src/errors.rs +++ b/compiler/rustc_privacy/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic}; +use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(SessionDiagnostic)] @@ -73,3 +73,19 @@ pub struct InPublicInterface<'a> { #[label(privacy::visibility_label)] pub vis_span: Span, } + +#[derive(LintDiagnostic)] +#[lint(privacy::from_private_dep_in_public_interface)] +pub struct FromPrivateDependencyInPublicInterface<'a> { + pub kind: &'a str, + pub descr: String, + pub krate: Symbol, +} + +#[derive(LintDiagnostic)] +#[lint(privacy::private_in_public_lint)] +pub struct PrivateInPublicLint<'a> { + pub vis_descr: &'static str, + pub kind: &'a str, + pub descr: String, +} diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 5b21c046647..9a835808d49 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -38,8 +38,8 @@ use std::ops::ControlFlow; use std::{cmp, fmt, mem}; use errors::{ - FieldIsPrivate, FieldIsPrivateLabel, InPublicInterface, InPublicInterfaceTraits, ItemIsPrivate, - UnnamedItemIsPrivate, + FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface, + InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, UnnamedItemIsPrivate, }; //////////////////////////////////////////////////////////////////////////////// @@ -1716,19 +1716,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { if self.leaks_private_dep(def_id) { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES, self.tcx.hir().local_def_id_to_hir_id(self.item_def_id), self.tcx.def_span(self.item_def_id.to_def_id()), - |lint| { - lint.build(&format!( - "{} `{}` from private dependency '{}' in public \ - interface", - kind, - descr, - self.tcx.crate_name(def_id.krate) - )) - .emit(); + FromPrivateDependencyInPublicInterface { + kind, + descr: descr.to_string(), + krate: self.tcx.crate_name(def_id.krate), }, ); } @@ -1754,12 +1749,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { } }; let span = self.tcx.def_span(self.item_def_id.to_def_id()); + let descr = descr.to_string(); if self.has_old_errors || self.in_assoc_ty || self.tcx.resolutions(()).has_pub_restricted { let descr = descr.to_string(); - let vis_span = self.tcx.def_span(def_id); + let vis_span = + self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)); if kind == "trait" { self.tcx.sess.emit_err(InPublicInterfaceTraits { span, @@ -1778,19 +1775,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { }); } } else { - let err_code = if kind == "trait" { "E0445" } else { "E0446" }; - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( lint::builtin::PRIVATE_IN_PUBLIC, hir_id, span, - |lint| { - lint.build(&format!( - "{} (error {})", - format!("{} {} `{}` in public interface", vis_descr, kind, descr), - err_code - )) - .emit(); - }, + PrivateInPublicLint { vis_descr, kind, descr }, ); } } From 88c11c5bfffea445c6cc49b62da17f172eb8f055 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 17:15:31 +0100 Subject: [PATCH 04/30] macros: support `MultiSpan` in diag derives Add support for `MultiSpan` with any of the attributes that work on a `Span` - requires that diagnostic logic generated for these attributes are emitted in the by-move block rather than the by-ref block that they would normally have been generated in. Signed-off-by: David Wood --- .../src/diagnostics/diagnostic_builder.rs | 139 ++++++++++-------- .../rustc_macros/src/diagnostics/utils.rs | 8 +- .../session-diagnostic/diagnostic-derive.rs | 15 +- .../diagnostic-derive.stderr | 6 +- .../subdiagnostic-derive.rs | 2 +- .../subdiagnostic-derive.stderr | 2 +- 6 files changed, 104 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 74ce1ab08c2..db3ba20d8f9 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -13,7 +13,8 @@ use quote::{format_ident, quote}; use std::collections::HashMap; use std::str::FromStr; use syn::{ - parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, + parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta, + Path, Type, }; use synstructure::{BindingInfo, Structure}; @@ -80,8 +81,8 @@ impl DiagnosticDeriveBuilder { } pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) { - // Keep track of which fields are subdiagnostics or have no attributes. - let mut subdiagnostics_or_empty = std::collections::HashSet::new(); + // Keep track of which fields need to be handled with a by-move binding. + let mut needs_moved = std::collections::HashSet::new(); // Generates calls to `span_label` and similar functions based on the attributes // on fields. Code for suggestions uses formatting machinery and the value of @@ -92,16 +93,11 @@ impl DiagnosticDeriveBuilder { let attrs = structure .clone() .filter(|field_binding| { - let attrs = &field_binding.ast().attrs; - - (!attrs.is_empty() - && attrs.iter().all(|attr| { - "subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string() - })) - || { - subdiagnostics_or_empty.insert(field_binding.binding.clone()); - false - } + let ast = &field_binding.ast(); + !self.needs_move(ast) || { + needs_moved.insert(field_binding.binding.clone()); + false + } }) .each(|field_binding| self.generate_field_attrs_code(field_binding)); @@ -111,12 +107,41 @@ impl DiagnosticDeriveBuilder { // attributes or a `#[subdiagnostic]` attribute then it must be passed as an // argument to the diagnostic so that it can be referred to by Fluent messages. let args = structure - .filter(|field_binding| subdiagnostics_or_empty.contains(&field_binding.binding)) + .filter(|field_binding| needs_moved.contains(&field_binding.binding)) .each(|field_binding| self.generate_field_attrs_code(field_binding)); (attrs, args) } + /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic + /// call (like `span_label`). + fn should_generate_set_arg(&self, field: &Field) -> bool { + field.attrs.is_empty() + } + + /// Returns `true` if `field` needs to have code generated in the by-move branch of the + /// generated derive rather than the by-ref branch. + fn needs_move(&self, field: &Field) -> bool { + let generates_set_arg = self.should_generate_set_arg(field); + let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]); + // FIXME(davidtwco): better support for one field needing to be in the by-move and + // by-ref branches. + let is_subdiagnostic = field + .attrs + .iter() + .map(|attr| attr.path.segments.last().unwrap().ident.to_string()) + .any(|attr| attr == "subdiagnostic"); + + // `set_arg` calls take their argument by-move.. + generates_set_arg + // If this is a `MultiSpan` field then it needs to be moved to be used by any + // attribute.. + || is_multispan + // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is + // unlikely to be `Copy`.. + || is_subdiagnostic + } + /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates /// diagnostic builder calls for setting error code and creating note/help messages. @@ -227,57 +252,55 @@ impl DiagnosticDeriveBuilder { let field = binding_info.ast(); let field_binding = &binding_info.binding; - let inner_ty = FieldInnerTy::from_type(&field.ty); - - // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than - // borrow it to avoid requiring clones - this must therefore be the last use of - // each field (for example, any formatting machinery that might refer to a field - // should be generated already). - if field.attrs.is_empty() { + if self.should_generate_set_arg(&field) { let diag = &self.diag; let ident = field.ident.as_ref().unwrap(); - quote! { + return quote! { #diag.set_arg( stringify!(#ident), #field_binding ); - } - } else { - field - .attrs - .iter() - .map(move |attr| { - let name = attr.path.segments.last().unwrap().ident.to_string(); - let (binding, needs_destructure) = match (name.as_str(), &inner_ty) { - // `primary_span` can accept a `Vec` so don't destructure that. - ("primary_span", FieldInnerTy::Vec(_)) => { - (quote! { #field_binding.clone() }, false) - } - // `subdiagnostics` are not derefed because they are bound by value. - ("subdiagnostic", _) => (quote! { #field_binding }, true), - _ => (quote! { *#field_binding }, true), - }; - - let generated_code = self - .generate_inner_field_code( - attr, - FieldInfo { - binding: binding_info, - ty: inner_ty.inner_type().unwrap_or(&field.ty), - span: &field.span(), - }, - binding, - ) - .unwrap_or_else(|v| v.to_compile_error()); - - if needs_destructure { - inner_ty.with(field_binding, generated_code) - } else { - generated_code - } - }) - .collect() + }; } + + let needs_move = self.needs_move(&field); + let inner_ty = FieldInnerTy::from_type(&field.ty); + + field + .attrs + .iter() + .map(move |attr| { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let needs_clone = + name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_)); + let (binding, needs_destructure) = if needs_clone { + // `primary_span` can accept a `Vec` so don't destructure that. + (quote! { #field_binding.clone() }, false) + } else if needs_move { + (quote! { #field_binding }, true) + } else { + (quote! { *#field_binding }, true) + }; + + let generated_code = self + .generate_inner_field_code( + attr, + FieldInfo { + binding: binding_info, + ty: inner_ty.inner_type().unwrap_or(&field.ty), + span: &field.span(), + }, + binding, + ) + .unwrap_or_else(|v| v.to_compile_error()); + + if needs_destructure { + inner_ty.with(field_binding, generated_code) + } else { + generated_code + } + }) + .collect() } fn generate_inner_field_code( diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 8977db4606c..002abb152f7 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -85,7 +85,13 @@ pub(crate) fn report_error_if_not_applied_to_span( attr: &Attribute, info: &FieldInfo<'_>, ) -> Result<(), DiagnosticDeriveError> { - report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`") + if !type_matches_path(&info.ty, &["rustc_span", "Span"]) + && !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"]) + { + report_type_error(attr, "`Span` or `MultiSpan`")?; + } + + Ok(()) } /// Inner type of a field and type of wrapper. diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 56e95d70fd5..b343bcb7721 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -23,7 +23,7 @@ extern crate rustc_middle; use rustc_middle::ty::Ty; extern crate rustc_errors; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan}; extern crate rustc_session; @@ -140,7 +140,7 @@ struct CodeNotProvided {} #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] struct MessageWrongType { #[primary_span] - //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span` + //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` foo: String, } @@ -165,7 +165,7 @@ struct ErrorWithField { #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] struct ErrorWithMessageAppliedToField { #[label(typeck::label)] - //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` name: String, } @@ -208,7 +208,7 @@ struct LabelOnSpan { #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] struct LabelOnNonSpan { #[label(typeck::label)] - //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` id: u32, } @@ -552,3 +552,10 @@ struct LintsGood { //~^ ERROR only `#[lint(..)]` is supported struct ErrorsBad { } + +#[derive(SessionDiagnostic)] +#[error(typeck::ambiguous_lifetime_bound, code = "E0123")] +struct ErrorWithMultiSpan { + #[primary_span] + span: MultiSpan, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index e282884289d..e2580c6485a 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -233,7 +233,7 @@ LL | | struct SlugNotProvided {} | = help: specify the slug as the first argument to the attribute, such as `#[error(typeck::example_error)]` -error: the `#[primary_span]` attribute can only be applied to fields of type `Span` +error: the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` --> $DIR/diagnostic-derive.rs:142:5 | LL | #[primary_span] @@ -247,7 +247,7 @@ LL | #[nonsense] | = help: only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes -error: the `#[label(...)]` attribute can only be applied to fields of type `Span` +error: the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` --> $DIR/diagnostic-derive.rs:167:5 | LL | #[label(typeck::label)] @@ -279,7 +279,7 @@ LL | #[derive(SessionDiagnostic)] = note: if you intended to print `}`, you can escape it using `}}` = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: the `#[label(...)]` attribute can only be applied to fields of type `Span` +error: the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` --> $DIR/diagnostic-derive.rs:210:5 | LL | #[label(typeck::label)] diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 6f4b6105b3e..cbb66c13c68 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -244,7 +244,7 @@ enum V { //~^ ERROR label without `#[primary_span]` field struct W { #[primary_span] - //~^ ERROR the `#[primary_span]` attribute can only be applied to fields of type `Span` + //~^ ERROR the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` span: String, } diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index f833bd210f7..a289c4fffd9 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -120,7 +120,7 @@ error: subdiagnostic kind not specified LL | B { | ^ -error: the `#[primary_span]` attribute can only be applied to fields of type `Span` +error: the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` --> $DIR/subdiagnostic-derive.rs:246:5 | LL | #[primary_span] From 81cf2294b4d912ec410696c5e2dec7659243d191 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 18:46:24 +0100 Subject: [PATCH 05/30] macros: support adding warnings to diags Both diagnostic and subdiagnostic derives were missing the ability to add warnings to diagnostics - this is made more difficult by the `warn` attribute already existing, so this name being unavailable for the derives to use. `#[warn_]` is used instead, which requires special-casing so that `{span_,}warn` is called instead of `{span_,}warn_`. Signed-off-by: David Wood --- .../src/diagnostics/diagnostic.rs | 2 +- .../src/diagnostics/diagnostic_builder.rs | 51 ++++++++++++------- .../rustc_macros/src/diagnostics/fluent.rs | 6 ++- .../src/diagnostics/subdiagnostic.rs | 4 ++ compiler/rustc_macros/src/lib.rs | 7 ++- .../session-diagnostic/diagnostic-derive.rs | 9 +++- .../diagnostic-derive.stderr | 4 +- .../subdiagnostic-derive.rs | 12 +++++ 8 files changed, 70 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 027f377b0ac..6b5b8b59320 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } (Some(DiagnosticDeriveKind::Lint), _) => { - span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported") + span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported") .help("use the `#[error(...)]` attribute to create a error") .emit(); return DiagnosticDeriveError::ErrorHandled.to_compile_error(); diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index db3ba20d8f9..5c5275b7cfb 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -8,7 +8,7 @@ use crate::diagnostics::utils::{ report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path, Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, }; -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; use std::collections::HashMap; use std::str::FromStr; @@ -156,7 +156,7 @@ impl DiagnosticDeriveBuilder { let name = name.as_str(); let meta = attr.parse_meta()?; - let is_help_or_note = matches!(name, "help" | "note"); + let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_"); let nested = match meta { // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or @@ -164,8 +164,12 @@ impl DiagnosticDeriveBuilder { Meta::List(MetaList { ref nested, .. }) => nested, // Subdiagnostics without spans can be applied to the type too, and these are just // paths: `#[help]` and `#[note]` - Meta::Path(_) if is_help_or_note => { - let fn_name = proc_macro2::Ident::new(name, attr.span()); + Meta::Path(_) if is_help_note_or_warn => { + let fn_name = if name == "warn_" { + Ident::new("warn", attr.span()) + } else { + Ident::new(name, attr.span()) + }; return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); }); } _ => throw_invalid_attr!(attr, &meta), @@ -177,9 +181,11 @@ impl DiagnosticDeriveBuilder { "error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)), "warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)), "lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)), - "help" | "note" => (), + "help" | "note" | "warn_" => (), _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("only `error`, `warning`, `help` and `note` are valid attributes") + diag.help( + "only `error`, `warning`, `help`, `note` and `warn_` are valid attributes", + ) }), } @@ -188,14 +194,16 @@ impl DiagnosticDeriveBuilder { let mut nested_iter = nested.into_iter(); if let Some(nested_attr) = nested_iter.next() { // Report an error if there are any other list items after the path. - if is_help_or_note && nested_iter.next().is_some() { + if is_help_note_or_warn && nested_iter.next().is_some() { throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("`help` and `note` struct attributes can only have one argument") + diag.help( + "`help`, `note` and `warn_` struct attributes can only have one argument", + ) }); } match nested_attr { - NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => { + NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => { let fn_name = proc_macro2::Ident::new(name, attr.span()); return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); }); } @@ -203,7 +211,7 @@ impl DiagnosticDeriveBuilder { self.slug.set_once((path.clone(), span)); } NestedMeta::Meta(meta @ Meta::NameValue(_)) - if !is_help_or_note + if !is_help_note_or_warn && meta.path().segments.last().unwrap().ident.to_string() == "code" => { // don't error for valid follow-up attributes @@ -347,10 +355,12 @@ impl DiagnosticDeriveBuilder { report_error_if_not_applied_to_span(attr, &info)?; Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label })) } - "note" | "help" => { - let path = match name { - "note" => parse_quote! { _subdiag::note }, - "help" => parse_quote! { _subdiag::help }, + "note" | "help" | "warn_" => { + let warn_ident = Ident::new("warn", Span::call_site()); + let (ident, path) = match name { + "note" => (ident, parse_quote! { _subdiag::note }), + "help" => (ident, parse_quote! { _subdiag::help }), + "warn_" => (&warn_ident, parse_quote! { _subdiag::warn }), _ => unreachable!(), }; if type_matches_path(&info.ty, &["rustc_span", "Span"]) { @@ -387,10 +397,10 @@ impl DiagnosticDeriveBuilder { "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => { return self.generate_inner_field_code_suggestion(attr, info); } - "label" | "help" | "note" => (), + "label" | "help" | "note" | "warn_" => (), _ => throw_invalid_attr!(attr, &meta, |diag| { diag.help( - "only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \ + "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \ valid field attributes", ) }), @@ -419,7 +429,14 @@ impl DiagnosticDeriveBuilder { Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) } "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)), - "note" | "help" => report_type_error(attr, "`Span` or `()`")?, + // `warn_` must be special-cased because the attribute `warn` already has meaning and + // so isn't used, despite the diagnostic API being named `warn`. + "warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self + .add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)), + "warn_" if type_is_unit(&info.ty) => { + Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg)) + } + "note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?, _ => unreachable!(), } } diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 2758fcd1310..1170d2b3c59 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok #generated pub mod _subdiag { - pub const note: crate::SubdiagnosticMessage = - crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note")); pub const help: crate::SubdiagnosticMessage = crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help")); + pub const note: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note")); + pub const warn: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn")); pub const label: crate::SubdiagnosticMessage = crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label")); pub const suggestion: crate::SubdiagnosticMessage = diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 2a5b6beba94..edf4dbed985 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -37,6 +37,8 @@ enum SubdiagnosticKind { Note, /// `#[help(...)]` Help, + /// `#[warn_(...)]` + Warn, /// `#[suggestion{,_short,_hidden,_verbose}]` Suggestion(SubdiagnosticSuggestionKind), } @@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind { "label" => Ok(SubdiagnosticKind::Label), "note" => Ok(SubdiagnosticKind::Note), "help" => Ok(SubdiagnosticKind::Help), + "warn_" => Ok(SubdiagnosticKind::Warn), "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)), "suggestion_short" => { Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short)) @@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind { SubdiagnosticKind::Label => write!(f, "label"), SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Help => write!(f, "help"), + SubdiagnosticKind::Warn => write!(f, "warn"), SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => { write!(f, "suggestion") } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 168530c54b9..ab509b26f1c 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -130,8 +130,9 @@ decl_derive!( warning, error, lint, - note, help, + note, + warn_, // field attributes skip_arg, primary_span, @@ -148,8 +149,9 @@ decl_derive!( warning, error, lint, - note, help, + note, + warn_, // field attributes skip_arg, primary_span, @@ -166,6 +168,7 @@ decl_derive!( label, help, note, + warn_, suggestion, suggestion_short, suggestion_hidden, diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index b343bcb7721..0a210cbdc94 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -538,7 +538,7 @@ struct LabelWithTrailingList { #[derive(SessionDiagnostic)] #[lint(typeck::ambiguous_lifetime_bound)] -//~^ ERROR only `#[error(..)]` and `#[warn(..)]` are supported +//~^ ERROR only `#[error(..)]` and `#[warning(..)]` are supported struct LintsBad { } @@ -559,3 +559,10 @@ struct ErrorWithMultiSpan { #[primary_span] span: MultiSpan, } + +#[derive(SessionDiagnostic)] +#[error(typeck::ambiguous_lifetime_bound, code = "E0123")] +#[warn_] +struct ErrorWithWarn { + val: String, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index e2580c6485a..c1080aa2452 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -21,7 +21,7 @@ error: `#[nonsense(...)]` is not a valid attribute LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: only `error`, `warning`, `help` and `note` are valid attributes + = help: only `error`, `warning`, `help`, `note` and `warn_` are valid attributes error: diagnostic kind not specified --> $DIR/diagnostic-derive.rs:53:1 @@ -363,7 +363,7 @@ error: `#[label(...)]` is not a valid attribute LL | #[label(typeck::label, foo("..."))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: only `#[error(..)]` and `#[warn(..)]` are supported +error: only `#[error(..)]` and `#[warning(..)]` are supported --> $DIR/diagnostic-derive.rs:540:1 | LL | / #[lint(typeck::ambiguous_lifetime_bound)] diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index cbb66c13c68..16da25c402b 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -508,3 +508,15 @@ enum AX { span: Span, } } + +#[derive(SessionSubdiagnostic)] +#[warn_(parser::add_paren)] +struct AY { +} + +#[derive(SessionSubdiagnostic)] +#[warn_(parser::add_paren)] +struct AZ { + #[primary_span] + span: Span, +} From 78b19a90b7d728f3bde6a70a2509ae177561ce5f Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 11 Jul 2022 18:59:04 +0100 Subject: [PATCH 06/30] passes: migrate half of `check_attr` Migrate half of the `rustc_passes::check_attr` diagnostics to using diagnostic derives and being translatable. --- Cargo.lock | 1 + .../locales/en-US/passes.ftl | 151 ++++ compiler/rustc_error_messages/src/lib.rs | 1 + compiler/rustc_errors/src/diagnostic.rs | 1 + compiler/rustc_passes/Cargo.toml | 1 + compiler/rustc_passes/src/check_attr.rs | 690 +++++++----------- compiler/rustc_passes/src/errors.rs | 362 +++++++++ compiler/rustc_passes/src/lib.rs | 3 +- src/test/rustdoc-ui/invalid-doc-attr.stderr | 10 +- .../ui/attributes/invalid-doc-attr.stderr | 10 +- .../ui/attributes/multiple-invalid.stderr | 4 +- ...issue-43106-gating-of-builtin-attrs.stderr | 22 +- .../ui/future-incompatible-lint-group.stderr | 2 +- src/test/ui/issues/issue-54044.stderr | 8 +- src/test/ui/issues/issue-78957.stderr | 12 +- src/test/ui/macros/issue-68060.stderr | 4 +- .../marker-attribute-on-non-trait.rs | 12 +- .../marker-attribute-on-non-trait.stderr | 12 +- .../invalid-attribute.rs | 4 +- .../invalid-attribute.stderr | 4 +- .../ui/rfc-2091-track-caller/only-for-fns.rs | 2 +- .../rfc-2091-track-caller/only-for-fns.stderr | 4 +- src/test/ui/rustdoc/doc_keyword.rs | 2 +- src/test/ui/rustdoc/doc_keyword.stderr | 6 +- .../target-feature/invalid-attribute.stderr | 32 +- .../rustc_must_implement_one_of_misuse.rs | 4 +- .../rustc_must_implement_one_of_misuse.stderr | 4 +- 27 files changed, 844 insertions(+), 524 deletions(-) create mode 100644 compiler/rustc_error_messages/locales/en-US/passes.ftl create mode 100644 compiler/rustc_passes/src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index 0b44201d56f..9a8d9d40b6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4239,6 +4239,7 @@ dependencies = [ "rustc_hir", "rustc_index", "rustc_lexer", + "rustc_macros", "rustc_middle", "rustc_serialize", "rustc_session", diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl new file mode 100644 index 00000000000..e4c9a4dad7b --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -0,0 +1,151 @@ +-passes-previously-accepted = + this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +-passes-see-issue = + see issue #{$issue} for more information + +passes-outer-crate-level-attr = + crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` + +passes-inner-crate-level-attr = + crate-level attribute should be in the root module + +passes-ignored-attr-with-macro = `#[{$sym}]` is ignored on struct fields, match arms and macro defs + .warn = {-passes-previously-accepted} + .note = {-passes-see-issue(issue: "80564")} + +passes-ignored-attr = `#[{$sym}]` is ignored on struct fields and match arms + .warn = {-passes-previously-accepted} + .note = {-passes-see-issue(issue: "80564")} + +passes-inline-ignored-function-prototype = `#[inline]` is ignored on function prototypes + +passes-inline-ignored-constants = `#[inline]` is ignored on constants + .warn = {-passes-previously-accepted} + .note = {-passes-see-issue(issue: "65833")} + +passes-inline-not-fn-or-closure = attribute should be applied to function or closure + .label = not a function or closure + +passes-no-coverage-ignored-function-prototype = `#[no_coverage]` is ignored on function prototypes + +passes-no-coverage-propagate = + `#[no_coverage]` does not propagate into items and must be applied to the contained functions directly + +passes-no-coverage-fn-defn = `#[no_coverage]` may only be applied to function definitions + +passes-no-coverage-not-coverable = `#[no_coverage]` must be applied to coverable code + .label = not coverable code + +passes-should-be-applied-to-fn = attribute should be applied to a function definition + .label = not a function definition + +passes-naked-tracked-caller = cannot use `#[track_caller]` with `#[naked]` + +passes-should-be-applied-to-struct-enum = attribute should be applied to a struct or enum + .label = not a struct or enum + +passes-should-be-applied-to-trait = attribute should be applied to a trait + .label = not a trait + +passes-target-feature-on-statement = {passes-should-be-applied-to-fn} + .warn = {-passes-previously-accepted} + .label = {passes-should-be-applied-to-fn.label} + +passes-should-be-applied-to-static = attribute should be applied to a static + .label = not a static + +passes-doc-expect-str = doc {$attr_name} attribute expects a string: #[doc({$attr_name} = "a")] + +passes-doc-alias-empty = {$attr_str} attribute cannot have empty value + +passes-doc-alias-bad-char = {$char_} character isn't allowed in {$attr_str} + +passes-doc-alias-start-end = {$attr_str} cannot start or end with ' ' + +passes-doc-alias-bad-location = {$attr_str} isn't allowed on {$location} + +passes-doc-alias-not-an-alias = {$attr_str} is the same as the item's name + +passes-doc-alias-duplicated = doc alias is duplicated + .label = first defined here + +passes-doc-alias-not-string-literal = `#[doc(alias("a"))]` expects string literals + +passes-doc-alias-malformed = + doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` + +passes-doc-keyword-empty-mod = `#[doc(keyword = "...")]` should be used on empty modules + +passes-doc-keyword-not-mod = `#[doc(keyword = "...")]` should be used on modules + +passes-doc-keyword-invalid-ident = `{$doc_keyword}` is not a valid identifier + +passes-doc-tuple-variadic-not-first = + `#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity + +passes-doc-keyword-only-impl = `#[doc(keyword = "...")]` should be used on impl blocks + +passes-doc-inline-conflict-first = this attribute... +passes-doc-inline-conflict-second = ...conflicts with this attribute +passes-doc-inline-conflict = conflicting doc inlining attributes + .help = remove one of the conflicting attributes + +passes-doc-inline-only-use = this attribute can only be applied to a `use` item + .label = only applicable on `use` items + .not-a-use-item-label = not a `use` item + .note = read for more information + +passes-doc-attr-not-crate-level = + `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute + +passes-attr-crate-level = this attribute can only be applied at the crate level + .suggestion = to apply to the crate, use an inner attribute + .help = to apply to the crate, use an inner attribute + .note = read for more information + +passes-doc-test-unknown = unknown `doc(test)` attribute `{$path}` + +passes-doc-test-takes-list = `#[doc(test(...)]` takes a list of attributes + +passes-doc-primitive = `doc(primitive)` should never have been stable + +passes-doc-test-unknown-any = unknown `doc` attribute `{$path}` + +passes-doc-test-unknown-spotlight = unknown `doc` attribute `{$path}` + .note = `doc(spotlight)` was renamed to `doc(notable_trait)` + .suggestion = use `notable_trait` instead + .no-op-note = `doc(spotlight)` is now a no-op + +passes-doc-test-unknown-include = unknown `doc` attribute `{$path}` + .suggestion = use `doc = include_str!` instead + +passes-doc-invalid = invalid `doc` attribute + +passes-pass-by-value = `pass_by_value` attribute should be applied to a struct, enum or type alias + .label = is not a struct, enum or type alias + +passes-allow-incoherent-impl = + `rustc_allow_incoherent_impl` attribute should be applied to impl items. + .label = the only currently supported targets are inherent methods + +passes-has-incoherent-inherent-impl = + `rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits. + .label = only adts, extern types and traits are supported + +passes-must-use-async = + `must_use` attribute on `async` functions applies to the anonymous `Future` returned by the function, not the value within + .label = this attribute does nothing, the `Future`s returned by async functions are already `must_use` + +passes-must-use-no-effect = `#[must_use]` has no effect when applied to {$article} {$target} + +passes-must-not-suspend = `must_not_suspend` attribute should be applied to a struct, enum, or trait + .label = is not a struct, enum, or trait + +passes-cold = {passes-should-be-applied-to-fn} + .warn = {-passes-previously-accepted} + .label = {passes-should-be-applied-to-fn.label} + +passes-link = attribute should be applied to an `extern` block with non-Rust ABI + .warn = {-passes-previously-accepted} + .label = not an `extern` block diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index d16171cb162..a3040f83fdf 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -37,6 +37,7 @@ fluent_messages! { expand => "../locales/en-US/expand.ftl", lint => "../locales/en-US/lint.ftl", parser => "../locales/en-US/parser.ftl", + passes => "../locales/en-US/passes.ftl", privacy => "../locales/en-US/privacy.ftl", typeck => "../locales/en-US/typeck.ftl", } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 0ce7f3c7e82..267beb51484 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -64,6 +64,7 @@ into_diagnostic_arg_using_display!( i128, u128, std::num::NonZeroU32, + hir::Target, Edition, Ident, ); diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index 676812db59a..faa9c493d88 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_session = { path = "../rustc_session" } rustc_target = { path = "../rustc_target" } +rustc_macros = { path = "../rustc_macros" } rustc_ast = { path = "../rustc_ast" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e626a1e4ed1..d96e7d3efe8 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -4,9 +4,10 @@ //! conflicts between multiple such attributes attached to the same //! item. +use crate::errors; use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; +use rustc_errors::{fluent, pluralize, struct_span_err, Applicability, MultiSpan}; use rustc_expand::base::resolve_path; use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP}; use rustc_hir as hir; @@ -175,16 +176,20 @@ impl CheckAttrVisitor<'_> { if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - let msg = match attr.style { - ast::AttrStyle::Outer => { - "crate-level attribute should be an inner attribute: add an exclamation \ - mark: `#![foo]`" - } - ast::AttrStyle::Inner => "crate-level attribute should be in the root module", - }; - lint.build(msg).emit(); - }); + match attr.style { + ast::AttrStyle::Outer => self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::OuterCrateLevelAttr, + ), + ast::AttrStyle::Inner => self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::InnerCrateLevelAttr, + ), + } } } @@ -209,37 +214,21 @@ impl CheckAttrVisitor<'_> { } fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[{sym}]` is ignored on struct fields, match arms and macro defs", - )) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #80564 \ - for more information", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredAttrWithMacro { sym }, + ); } fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!("`#[{sym}]` is ignored on struct fields and match arms")) - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #80564 \ - for more information", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredAttr { sym }, + ); } /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. @@ -249,9 +238,12 @@ impl CheckAttrVisitor<'_> { | Target::Closure | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[inline]` is ignored on function prototypes").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredInlineAttrFnProto, + ); true } // FIXME(#65833): We permit associated consts to have an `#[inline]` attribute with @@ -259,19 +251,12 @@ impl CheckAttrVisitor<'_> { // accidentally, to to be compatible with crates depending on them, we can't throw an // error here. Target::AssocConst => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[inline]` is ignored on constants") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .note( - "see issue #65833 \ - for more information", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredInlineAttrConstants, + ); true } // FIXME(#80564): Same for fields, arms, and macro defs @@ -280,14 +265,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - struct_span_err!( - self.tcx.sess, - attr.span, - E0518, - "attribute should be applied to function or closure", - ) - .span_label(span, "not a function or closure") - .emit(); + self.tcx.sess.emit_err(errors::InlineNotFnOrClosure { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -309,36 +290,40 @@ impl CheckAttrVisitor<'_> { // function prototypes can't be covered Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[no_coverage]` is ignored on function prototypes").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredNoCoverageFnProto, + ); true } Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[no_coverage]` does not propagate into items and must be applied to the contained functions directly").emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredNoCoveragePropagate, + ); true } Target::Expression | Target::Statement | Target::Arm => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("`#[no_coverage]` may only be applied to function definitions") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::IgnoredNoCoverageFnDefn, + ); true } _ => { - struct_span_err!( - self.tcx.sess, - attr.span, - E0788, - "`#[no_coverage]` must be applied to coverable code", - ) - .span_label(span, "not coverable code") - .emit(); + self.tcx.sess.emit_err(errors::IgnoredNoCoverageNotCoverable { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -389,14 +374,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to a function definition", - ) - .span_label(span, "not a function definition") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -408,14 +389,10 @@ impl CheckAttrVisitor<'_> { Target::Fn | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true, _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "attribute should be applied to a function definition", - ) - .span_label(span, "not a function definition") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -432,13 +409,7 @@ impl CheckAttrVisitor<'_> { ) -> bool { match target { _ if attrs.iter().any(|attr| attr.has_name(sym::naked)) => { - struct_span_err!( - self.tcx.sess, - attr_span, - E0736, - "cannot use `#[track_caller]` with `#[naked]`", - ) - .emit(); + self.tcx.sess.emit_err(errors::NakedTrackedCaller { attr_span }); false } Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true, @@ -453,14 +424,9 @@ impl CheckAttrVisitor<'_> { true } _ => { - struct_span_err!( - self.tcx.sess, - attr_span, - E0739, - "attribute should be applied to function" - ) - .span_label(span, "not a function") - .emit(); + self.tcx + .sess + .emit_err(errors::TrackedCallerWrongLocation { attr_span, defn_span: span }); false } } @@ -485,14 +451,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - struct_span_err!( - self.tcx.sess, - attr.span, - E0701, - "attribute can only be applied to a struct or enum" - ) - .span_label(span, "not a struct or enum") - .emit(); + self.tcx.sess.emit_err(errors::NonExhaustiveWrongLocation { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -511,11 +473,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute can only be applied to a trait") - .span_label(span, "not a trait") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -531,11 +492,10 @@ impl CheckAttrVisitor<'_> { match target { Target::Trait => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute can only be applied to a trait") - .span_label(span, "not a trait") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -555,16 +515,12 @@ impl CheckAttrVisitor<'_> { // FIXME: #[target_feature] was previously erroneously allowed on statements and some // crates used this, so only emit a warning. Target::Statement => { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute should be applied to a function") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a function") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::TargetFeatureOnStatement, + ); true } // FIXME(#80564): We permit struct fields, match arms and macro defs to have an @@ -576,11 +532,10 @@ impl CheckAttrVisitor<'_> { true } _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a function") - .span_label(span, "not a function") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToFn { + attr_span: attr.span, + defn_span: span, + }); false } } @@ -591,24 +546,17 @@ impl CheckAttrVisitor<'_> { match target { Target::ForeignStatic | Target::Static => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "attribute should be applied to a static") - .span_label(span, "not a static") - .emit(); + self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToStatic { + attr_span: attr.span, + defn_span: span, + }); false } } } fn doc_attr_str_error(&self, meta: &NestedMetaItem, attr_name: &str) { - self.tcx - .sess - .struct_span_err( - meta.span(), - &format!("doc {0} attribute expects a string: #[doc({0} = \"a\")]", attr_name), - ) - .emit(); + self.tcx.sess.emit_err(errors::DocExpectStr { attr_span: meta.span(), attr_name }); } fn check_doc_alias_value( @@ -621,22 +569,12 @@ impl CheckAttrVisitor<'_> { aliases: &mut FxHashMap, ) -> bool { let tcx = self.tcx; - let err_fn = move |span: Span, msg: &str| { - tcx.sess.span_err( - span, - &format!( - "`#[doc(alias{})]` {}", - if is_list { "(\"...\")" } else { " = \"...\"" }, - msg, - ), - ); - false - }; + let span = meta.name_value_literal_span().unwrap_or_else(|| meta.span()); + let attr_str = + &format!("`#[doc(alias{})]`", if is_list { "(\"...\")" } else { " = \"...\"" }); if doc_alias == kw::Empty { - return err_fn( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - "attribute cannot have empty value", - ); + tcx.sess.emit_err(errors::DocAliasEmpty { span, attr_str }); + return false; } let doc_alias_str = doc_alias.as_str(); @@ -644,23 +582,16 @@ impl CheckAttrVisitor<'_> { .chars() .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) { - self.tcx.sess.span_err( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - &format!( - "{:?} character isn't allowed in `#[doc(alias{})]`", - c, - if is_list { "(\"...\")" } else { " = \"...\"" }, - ), - ); + tcx.sess.emit_err(errors::DocAliasBadChar { span, attr_str, char_: c }); return false; } if doc_alias_str.starts_with(' ') || doc_alias_str.ends_with(' ') { - return err_fn( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - "cannot start or end with ' '", - ); + tcx.sess.emit_err(errors::DocAliasStartEnd { span, attr_str }); + return false; } - if let Some(err) = match target { + + let span = meta.span(); + if let Some(location) = match target { Target::Impl => Some("implementation block"), Target::ForeignMod => Some("extern block"), Target::AssocTy => { @@ -686,19 +617,21 @@ impl CheckAttrVisitor<'_> { Target::Param => return false, _ => None, } { - return err_fn(meta.span(), &format!("isn't allowed on {}", err)); + tcx.sess.emit_err(errors::DocAliasBadLocation { span, attr_str, location }); + return false; } let item_name = self.tcx.hir().name(hir_id); if item_name == doc_alias { - return err_fn(meta.span(), "is the same as the item's name"); + tcx.sess.emit_err(errors::DocAliasNotAnAlias { span, attr_str }); + return false; } - let span = meta.span(); if let Err(entry) = aliases.try_insert(doc_alias_str.to_owned(), span) { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, span, |lint| { - lint.build("doc alias is duplicated") - .span_label(*entry.entry.get(), "first defined here") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + span, + errors::DocAliasDuplicated { first_defn: *entry.entry.get() }, + ); } true } @@ -723,22 +656,12 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err( - v.span(), - "`#[doc(alias(\"a\"))]` expects string literals", - ) - .emit(); + .emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); errors += 1; } }, None => { - self.tcx - .sess - .struct_span_err( - v.span(), - "`#[doc(alias(\"a\"))]` expects string literals", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocAliasNotStringLiteral { span: v.span() }); errors += 1; } } @@ -747,14 +670,7 @@ impl CheckAttrVisitor<'_> { } else if let Some(doc_alias) = meta.value_str() { self.check_doc_alias_value(meta, doc_alias, hir_id, target, false, aliases) } else { - self.tcx - .sess - .struct_span_err( - meta.span(), - "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of \ - strings `#[doc(alias(\"a\", \"b\"))]`", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocAliasMalformed { span: meta.span() }); false } } @@ -771,35 +687,20 @@ impl CheckAttrVisitor<'_> { }) { Some(ItemKind::Mod(ref module)) => { if !module.item_ids.is_empty() { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(keyword = \"...\")]` can only be used on empty modules", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordEmptyMod { span: meta.span() }); return false; } } _ => { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(keyword = \"...\")]` can only be used on modules", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordNotMod { span: meta.span() }); return false; } } if !rustc_lexer::is_ident(doc_keyword.as_str()) { - self.tcx - .sess - .struct_span_err( - meta.name_value_literal_span().unwrap_or_else(|| meta.span()), - &format!("`{doc_keyword}` is not a valid identifier"), - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordInvalidIdent { + span: meta.name_value_literal_span().unwrap_or_else(|| meta.span()), + doc_keyword, + }); return false; } true @@ -812,24 +713,12 @@ impl CheckAttrVisitor<'_> { }) { Some(ItemKind::Impl(ref i)) => { if !matches!(&i.self_ty.kind, hir::TyKind::Tup([_])) { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(tuple_variadic)]` must be used on the first of a set of tuple trait impls with varying arity", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocTupleVariadicNotFirst { span: meta.span() }); return false; } } _ => { - self.tcx - .sess - .struct_span_err( - meta.span(), - "`#[doc(keyword = \"...\")]` can only be used on impl blocks", - ) - .emit(); + self.tcx.sess.emit_err(errors::DocKeywordOnlyImpl { span: meta.span() }); return false; } } @@ -858,13 +747,9 @@ impl CheckAttrVisitor<'_> { if let Some((prev_inline, prev_span)) = *specified_inline { if do_inline != prev_inline { let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]); - spans.push_span_label(prev_span, "this attribute..."); - spans.push_span_label(meta.span(), "...conflicts with this attribute"); - self.tcx - .sess - .struct_span_err(spans, "conflicting doc inlining attributes") - .help("remove one of the conflicting attributes") - .emit(); + spans.push_span_label(prev_span, fluent::passes::doc_inline_conflict_first); + spans.push_span_label(meta.span(), fluent::passes::doc_inline_conflict_second); + self.tcx.sess.emit_err(errors::DocKeywordConflict { spans }); return false; } true @@ -873,23 +758,14 @@ impl CheckAttrVisitor<'_> { true } } else { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), - |lint| { - let mut err = lint.build( - "this attribute can only be applied to a `use` item", - ); - err.span_label(meta.span(), "only applicable on `use` items"); - if attr.style == AttrStyle::Outer { - err.span_label( - self.tcx.hir().span(hir_id), - "not a `use` item", - ); - } - err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information") - .emit(); + errors::DocInlineOnlyUse { + attr_span: meta.span(), + item_span: (attr.style == AttrStyle::Outer) + .then(|| self.tcx.hir().span(hir_id)), }, ); false @@ -904,15 +780,7 @@ impl CheckAttrVisitor<'_> { attr_name: &str, ) -> bool { if CRATE_HIR_ID == hir_id { - self.tcx - .sess - .struct_span_err( - meta.span(), - &format!( - "`#![doc({attr_name} = \"...\")]` isn't allowed as a crate-level attribute", - ), - ) - .emit(); + self.tcx.sess.emit_err(errors::DocAttrNotCrateLevel { span: meta.span(), attr_name }); return false; } true @@ -926,36 +794,25 @@ impl CheckAttrVisitor<'_> { hir_id: HirId, ) -> bool { if hir_id != CRATE_HIR_ID { - self.tcx.struct_span_lint_hir( - INVALID_DOC_ATTRIBUTES, - hir_id, - meta.span(), - |lint| { - let mut err = lint.build( - "this attribute can only be applied at the crate level", - ); - if attr.style == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID { - if let Ok(mut src) = - self.tcx.sess.source_map().span_to_snippet(attr.span) - { - src.insert(1, '!'); - err.span_suggestion_verbose( - attr.span, - "to apply to the crate, use an inner attribute", - src, - Applicability::MaybeIncorrect, - ); - } else { - err.span_help( - attr.span, - "to apply to the crate, use an inner attribute", - ); - } + self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| { + let mut err = lint.build(fluent::passes::attr_crate_level); + if attr.style == AttrStyle::Outer + && self.tcx.hir().get_parent_item(hir_id) == CRATE_DEF_ID + { + if let Ok(mut src) = self.tcx.sess.source_map().span_to_snippet(attr.span) { + src.insert(1, '!'); + err.span_suggestion_verbose( + attr.span, + fluent::passes::suggestion, + src, + Applicability::MaybeIncorrect, + ); + } else { + err.span_help(attr.span, fluent::passes::help); } - err.note("read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information") - .emit(); - }, - ); + } + err.note(fluent::passes::note).emit(); + }); return false; } true @@ -970,18 +827,14 @@ impl CheckAttrVisitor<'_> { match i_meta.name_or_empty() { sym::attr | sym::no_crate_inject => {} _ => { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, i_meta.span(), - |lint| { - lint.build(&format!( - "unknown `doc(test)` attribute `{}`", - rustc_ast_pretty::pprust::path_to_string( - &i_meta.meta_item().unwrap().path - ), - )) - .emit(); + errors::DocTestUnknown { + path: rustc_ast_pretty::pprust::path_to_string( + &i_meta.meta_item().unwrap().path, + ), }, ); is_valid = false; @@ -989,9 +842,12 @@ impl CheckAttrVisitor<'_> { } } } else { - self.tcx.struct_span_lint_hir(INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), |lint| { - lint.build("`#[doc(test(...)]` takes a list of attributes").emit(); - }); + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + meta.span(), + errors::DocTestTakesList, + ); is_valid = false; } is_valid @@ -1093,79 +949,66 @@ impl CheckAttrVisitor<'_> { sym::primitive => { if !self.tcx.features().rustdoc_internals { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, i_meta.span, - |lint| { - let mut diag = lint.build( - "`doc(primitive)` should never have been stable", - ); - diag.emit(); - }, + errors::DocPrimitive, ); } } _ => { - self.tcx.struct_span_lint_hir( - INVALID_DOC_ATTRIBUTES, - hir_id, - i_meta.span, - |lint| { - let mut diag = lint.build(&format!( - "unknown `doc` attribute `{}`", - rustc_ast_pretty::pprust::path_to_string(&i_meta.path), - )); - if i_meta.has_name(sym::spotlight) { - diag.note( - "`doc(spotlight)` was renamed to `doc(notable_trait)`", - ); - diag.span_suggestion_short( - i_meta.span, - "use `notable_trait` instead", - "notable_trait", - Applicability::MachineApplicable, - ); - diag.note("`doc(spotlight)` is now a no-op"); + let path = rustc_ast_pretty::pprust::path_to_string(&i_meta.path); + if i_meta.has_name(sym::spotlight) { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownSpotlight { + path, + span: i_meta.span } - if i_meta.has_name(sym::include) { - if let Some(value) = i_meta.value_str() { - // if there are multiple attributes, the suggestion would suggest deleting all of them, which is incorrect - let applicability = if list.len() == 1 { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - let inner = if attr.style == AttrStyle::Inner { - "!" - } else { - "" - }; - diag.span_suggestion( - attr.meta().unwrap().span, - "use `doc = include_str!` instead", - format!( - "#{inner}[doc = include_str!(\"{value}\")]", - ), - applicability, - ); - } + ); + } else if i_meta.has_name(sym::include) && + let Some(value) = i_meta.value_str() { + let applicability = if list.len() == 1 { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + // If there are multiple attributes, the suggestion would suggest + // deleting all of them, which is incorrect. + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownInclude { + path, + value: value.to_string(), + inner: (attr.style == AttrStyle::Inner) + .then_some("!") + .unwrap_or(""), + sugg: (attr.meta().unwrap().span, applicability), } - diag.emit(); - }, - ); + ); + } else { + self.tcx.emit_spanned_lint( + INVALID_DOC_ATTRIBUTES, + hir_id, + i_meta.span, + errors::DocTestUnknownAny { path } + ); + } is_valid = false; } } } else { - self.tcx.struct_span_lint_hir( + self.tcx.emit_spanned_lint( INVALID_DOC_ATTRIBUTES, hir_id, meta.span(), - |lint| { - lint.build("invalid `doc` attribute").emit(); - }, + errors::DocInvalid, ); is_valid = false; } @@ -1180,14 +1023,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Struct | Target::Enum | Target::TyAlias => true, _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "`pass_by_value` attribute should be applied to a struct, enum or type alias.", - ) - .span_label(span, "is not a struct, enum or type alias") - .emit(); + self.tcx.sess.emit_err(errors::PassByValue { attr_span: attr.span, span }); false } } @@ -1197,14 +1033,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Method(MethodKind::Inherent) => true, _ => { - self.tcx - .sess - .struct_span_err( - attr.span, - "`rustc_allow_incoherent_impl` attribute should be applied to impl items.", - ) - .span_label(span, "the only currently supported targets are inherent methods") - .emit(); + self.tcx.sess.emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span }); false } } @@ -1223,12 +1052,7 @@ impl CheckAttrVisitor<'_> { _ => { self.tcx .sess - .struct_span_err( - attr.span, - "`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.", - ) - .span_label(span, "only adts, extern types and traits are supported") - .emit(); + .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span }); false } } @@ -1238,19 +1062,12 @@ impl CheckAttrVisitor<'_> { fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { let node = self.tcx.hir().get(hir_id); if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build( - "`must_use` attribute on `async` functions \ - applies to the anonymous `Future` returned by the \ - function, not the value within", - ) - .span_label( - span, - "this attribute does nothing, the `Future`s \ - returned by async functions are already `must_use`", - ) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::MustUseAsync { span } + ); } if !matches!( @@ -1278,12 +1095,12 @@ impl CheckAttrVisitor<'_> { _ => "a", }; - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build(&format!( - "`#[must_use]` has no effect when applied to {article} {target}" - )) - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::MustUseNoEffect { article, target }, + ); } // For now, its always valid @@ -1295,11 +1112,7 @@ impl CheckAttrVisitor<'_> { match target { Target::Struct | Target::Enum | Target::Union | Target::Trait => true, _ => { - self.tcx - .sess - .struct_span_err(attr.span, "`must_not_suspend` attribute should be applied to a struct, enum, or trait") - .span_label(span, "is not a struct, enum, or trait") - .emit(); + self.tcx.sess.emit_err(errors::MustNotSuspend { attr_span: attr.span, span }); false } } @@ -1319,16 +1132,12 @@ impl CheckAttrVisitor<'_> { _ => { // FIXME: #[cold] was previously allowed on non-functions and some crates used // this, so only emit a warning. - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build("attribute should be applied to a function") - .warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ) - .span_label(span, "not a function") - .emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Cold { span }, + ); } } } @@ -1343,19 +1152,12 @@ impl CheckAttrVisitor<'_> { return; } - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - let mut diag = - lint.build("attribute should be applied to an `extern` block with non-Rust ABI"); - diag.warn( - "this was previously accepted by the compiler but is \ - being phased out; it will become a hard error in \ - a future release!", - ); - if target != Target::ForeignMod { - diag.span_label(span, "not an `extern` block"); - } - diag.emit(); - }); + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::Link { span: (target != Target::ForeignMod).then_some(span) }, + ); } /// Checks if `#[link_name]` is applied to an item other than a foreign function or static. diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs new file mode 100644 index 00000000000..f8e8720ab54 --- /dev/null +++ b/compiler/rustc_passes/src/errors.rs @@ -0,0 +1,362 @@ +use rustc_errors::{Applicability, MultiSpan}; +use rustc_macros::{LintDiagnostic, SessionDiagnostic}; +use rustc_span::{Span, Symbol}; + +#[derive(LintDiagnostic)] +#[lint(passes::outer_crate_level_attr)] +pub struct OuterCrateLevelAttr; + +#[derive(LintDiagnostic)] +#[lint(passes::inner_crate_level_attr)] +pub struct InnerCrateLevelAttr; + +#[derive(LintDiagnostic)] +#[lint(passes::ignored_attr_with_macro)] +pub struct IgnoredAttrWithMacro<'a> { + pub sym: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::ignored_attr)] +pub struct IgnoredAttr<'a> { + pub sym: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::inline_ignored_function_prototype)] +pub struct IgnoredInlineAttrFnProto; + +#[derive(LintDiagnostic)] +#[lint(passes::inline_ignored_constants)] +#[warn_] +#[note] +pub struct IgnoredInlineAttrConstants; + +#[derive(SessionDiagnostic)] +#[error(passes::inline_not_fn_or_closure, code = "E0518")] +pub struct InlineNotFnOrClosure { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::no_coverage_ignored_function_prototype)] +pub struct IgnoredNoCoverageFnProto; + +#[derive(LintDiagnostic)] +#[lint(passes::no_coverage_propagate)] +pub struct IgnoredNoCoveragePropagate; + +#[derive(LintDiagnostic)] +#[lint(passes::no_coverage_fn_defn)] +pub struct IgnoredNoCoverageFnDefn; + +#[derive(SessionDiagnostic)] +#[error(passes::no_coverage_not_coverable, code = "E0788")] +pub struct IgnoredNoCoverageNotCoverable { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_fn)] +pub struct AttrShouldBeAppliedToFn { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::naked_tracked_caller, code = "E0736")] +pub struct NakedTrackedCaller { + #[primary_span] + pub attr_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_fn, code = "E0739")] +pub struct TrackedCallerWrongLocation { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_struct_enum, code = "E0701")] +pub struct NonExhaustiveWrongLocation { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_trait)] +pub struct AttrShouldBeAppliedToTrait { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::target_feature_on_statement)] +pub struct TargetFeatureOnStatement; + +#[derive(SessionDiagnostic)] +#[error(passes::should_be_applied_to_static)] +pub struct AttrShouldBeAppliedToStatic { + #[primary_span] + pub attr_span: Span, + #[label] + pub defn_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_expect_str)] +pub struct DocExpectStr<'a> { + #[primary_span] + pub attr_span: Span, + pub attr_name: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_empty)] +pub struct DocAliasEmpty<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_bad_char)] +pub struct DocAliasBadChar<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, + pub char_: char, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_start_end)] +pub struct DocAliasStartEnd<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_bad_location)] +pub struct DocAliasBadLocation<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, + pub location: &'a str, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_not_an_alias)] +pub struct DocAliasNotAnAlias<'a> { + #[primary_span] + pub span: Span, + pub attr_str: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_alias_duplicated)] +pub struct DocAliasDuplicated { + #[label] + pub first_defn: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_not_string_literal)] +pub struct DocAliasNotStringLiteral { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_alias_malformed)] +pub struct DocAliasMalformed { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_empty_mod)] +pub struct DocKeywordEmptyMod { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_not_mod)] +pub struct DocKeywordNotMod { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_invalid_ident)] +pub struct DocKeywordInvalidIdent { + #[primary_span] + pub span: Span, + pub doc_keyword: Symbol, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_tuple_variadic_not_first)] +pub struct DocTupleVariadicNotFirst { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_keyword_only_impl)] +pub struct DocKeywordOnlyImpl { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_inline_conflict)] +#[help] +pub struct DocKeywordConflict { + #[primary_span] + pub spans: MultiSpan, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_inline_only_use)] +#[note] +pub struct DocInlineOnlyUse { + #[label] + pub attr_span: Span, + #[label(passes::not_a_use_item_label)] + pub item_span: Option, +} + +#[derive(SessionDiagnostic)] +#[error(passes::doc_attr_not_crate_level)] +pub struct DocAttrNotCrateLevel<'a> { + #[primary_span] + pub span: Span, + pub attr_name: &'a str, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown)] +pub struct DocTestUnknown { + pub path: String, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_takes_list)] +pub struct DocTestTakesList; + +#[derive(LintDiagnostic)] +#[lint(passes::doc_primitive)] +pub struct DocPrimitive; + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown_any)] +pub struct DocTestUnknownAny { + pub path: String, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown_spotlight)] +#[note] +#[note(passes::no_op_note)] +pub struct DocTestUnknownSpotlight { + pub path: String, + #[suggestion_short(applicability = "machine-applicable", code = "notable_trait")] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_test_unknown_include)] +pub struct DocTestUnknownInclude { + pub path: String, + pub value: String, + pub inner: &'static str, + #[suggestion(code = "#{inner}[doc = include_str!(\"{value}\")]")] + pub sugg: (Span, Applicability), +} + +#[derive(LintDiagnostic)] +#[lint(passes::doc_invalid)] +pub struct DocInvalid; + +#[derive(SessionDiagnostic)] +#[error(passes::pass_by_value)] +pub struct PassByValue { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::allow_incoherent_impl)] +pub struct AllowIncoherentImpl { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(passes::has_incoherent_inherent_impl)] +pub struct HasIncoherentInherentImpl { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::must_use_async)] +pub struct MustUseAsync { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::must_use_no_effect)] +pub struct MustUseNoEffect { + pub article: &'static str, + pub target: rustc_hir::Target, +} + +#[derive(SessionDiagnostic)] +#[error(passes::must_not_suspend)] +pub struct MustNotSuspend { + #[primary_span] + pub attr_span: Span, + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::cold)] +#[warn_] +pub struct Cold { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[lint(passes::link)] +#[warn_] +pub struct Link { + #[label] + pub span: Option, +} diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 497c0931c21..7b2f83958af 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -7,8 +7,8 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(iter_intersperse)] -#![feature(let_else)] #![feature(let_chains)] +#![feature(let_else)] #![feature(map_try_insert)] #![feature(min_specialization)] #![feature(try_blocks)] @@ -27,6 +27,7 @@ pub mod dead; mod debugger_visualizer; mod diagnostic_items; pub mod entry; +mod errors; pub mod hir_id_validator; pub mod hir_stats; mod lang_items; diff --git a/src/test/rustdoc-ui/invalid-doc-attr.stderr b/src/test/rustdoc-ui/invalid-doc-attr.stderr index 55006b2087e..a4fa3817905 100644 --- a/src/test/rustdoc-ui/invalid-doc-attr.stderr +++ b/src/test/rustdoc-ui/invalid-doc-attr.stderr @@ -12,7 +12,7 @@ LL | #![deny(warnings)] = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information help: to apply to the crate, use an inner attribute | LL | #![doc(test(no_crate_inject))] @@ -29,7 +29,7 @@ LL | pub fn foo() {} | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information + = note: read for more information error: this attribute can only be applied at the crate level --> $DIR/invalid-doc-attr.rs:15:12 @@ -39,7 +39,7 @@ LL | #![doc(test(no_crate_inject))] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: conflicting doc inlining attributes --> $DIR/invalid-doc-attr.rs:28:7 @@ -59,7 +59,7 @@ LL | #[doc(test(no_crate_inject))] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: this attribute can only be applied to a `use` item --> $DIR/invalid-doc-attr.rs:22:11 @@ -72,7 +72,7 @@ LL | pub fn baz() {} | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information + = note: read for more information error: aborting due to 6 previous errors diff --git a/src/test/ui/attributes/invalid-doc-attr.stderr b/src/test/ui/attributes/invalid-doc-attr.stderr index 55006b2087e..a4fa3817905 100644 --- a/src/test/ui/attributes/invalid-doc-attr.stderr +++ b/src/test/ui/attributes/invalid-doc-attr.stderr @@ -12,7 +12,7 @@ LL | #![deny(warnings)] = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(warnings)]` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information help: to apply to the crate, use an inner attribute | LL | #![doc(test(no_crate_inject))] @@ -29,7 +29,7 @@ LL | pub fn foo() {} | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information + = note: read for more information error: this attribute can only be applied at the crate level --> $DIR/invalid-doc-attr.rs:15:12 @@ -39,7 +39,7 @@ LL | #![doc(test(no_crate_inject))] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: conflicting doc inlining attributes --> $DIR/invalid-doc-attr.rs:28:7 @@ -59,7 +59,7 @@ LL | #[doc(test(no_crate_inject))] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: this attribute can only be applied to a `use` item --> $DIR/invalid-doc-attr.rs:22:11 @@ -72,7 +72,7 @@ LL | pub fn baz() {} | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#inline-and-no_inline for more information + = note: read for more information error: aborting due to 6 previous errors diff --git a/src/test/ui/attributes/multiple-invalid.stderr b/src/test/ui/attributes/multiple-invalid.stderr index 9bd29f15dbc..a8dba0ba37d 100644 --- a/src/test/ui/attributes/multiple-invalid.stderr +++ b/src/test/ui/attributes/multiple-invalid.stderr @@ -7,14 +7,14 @@ LL | #[inline] LL | const FOO: u8 = 0; | ------------------ not a function or closure -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/multiple-invalid.rs:6:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | const FOO: u8 = 0; - | ------------------ not a function + | ------------------ not a function definition error: aborting due to 2 previous errors diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 3f838fcf523..5d6796b4944 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -259,7 +259,7 @@ warning: crate-level attribute should be an inner attribute: add an exclamation LL | #[no_std] | ^^^^^^^^^ -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:453:1 | LL | #[cold] @@ -272,7 +272,7 @@ LL | | mod inner { #![cold] } ... | LL | | LL | | } - | |_- not a function + | |_- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! @@ -399,7 +399,7 @@ warning: `#[proc_macro_derive]` only has an effect on functions LL | #![proc_macro_derive()] | ^^^^^^^^^^^^^^^^^^^^^^^ -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1 | LL | #![cold] @@ -743,35 +743,35 @@ warning: crate-level attribute should be an inner attribute: add an exclamation LL | #[no_std] impl S { } | ^^^^^^^^^ -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:459:17 | LL | mod inner { #![cold] } - | ------------^^^^^^^^-- not a function + | ------------^^^^^^^^-- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:466:5 | LL | #[cold] struct S; - | ^^^^^^^ --------- not a function + | ^^^^^^^ --------- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:471:5 | LL | #[cold] type T = S; - | ^^^^^^^ ----------- not a function + | ^^^^^^^ ----------- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: attribute should be applied to a function +warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:476:5 | LL | #[cold] impl S { } - | ^^^^^^^ ---------- not a function + | ^^^^^^^ ---------- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/src/test/ui/future-incompatible-lint-group.stderr b/src/test/ui/future-incompatible-lint-group.stderr index d822847a7a5..8f6dde665e6 100644 --- a/src/test/ui/future-incompatible-lint-group.stderr +++ b/src/test/ui/future-incompatible-lint-group.stderr @@ -22,7 +22,7 @@ LL | #![deny(future_incompatible)] = note: `#[deny(invalid_doc_attributes)]` implied by `#[deny(future_incompatible)]` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #82730 - = note: read https://doc.rust-lang.org/nightly/rustdoc/the-doc-attribute.html#at-the-crate-level for more information + = note: read for more information error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/issues/issue-54044.stderr b/src/test/ui/issues/issue-54044.stderr index 0200a6a629d..100965de1aa 100644 --- a/src/test/ui/issues/issue-54044.stderr +++ b/src/test/ui/issues/issue-54044.stderr @@ -1,11 +1,11 @@ -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-54044.rs:3:1 | LL | #[cold] | ^^^^^^^ ... LL | struct Foo; - | ----------- not a function + | ----------- not a function definition | note: the lint level is defined here --> $DIR/issue-54044.rs:1:9 @@ -14,14 +14,14 @@ LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-54044.rs:9:5 | LL | #[cold] | ^^^^^^^ ... LL | 5; - | - not a function + | - not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/src/test/ui/issues/issue-78957.stderr b/src/test/ui/issues/issue-78957.stderr index fa2eaab5b41..45fa69d6fd7 100644 --- a/src/test/ui/issues/issue-78957.stderr +++ b/src/test/ui/issues/issue-78957.stderr @@ -4,11 +4,11 @@ error[E0518]: attribute should be applied to function or closure LL | pub struct Foo<#[inline] const N: usize>; | ^^^^^^^^^ -------------- not a function or closure -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-78957.rs:7:16 | LL | pub struct Bar<#[cold] const N: usize>; - | ^^^^^^^ -------------- not a function + | ^^^^^^^ -------------- not a function definition | note: the lint level is defined here --> $DIR/issue-78957.rs:1:9 @@ -29,11 +29,11 @@ error[E0518]: attribute should be applied to function or closure LL | pub struct Foo2<#[inline] 'a>(PhantomData<&'a ()>); | ^^^^^^^^^ -- not a function or closure -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-78957.rs:15:17 | LL | pub struct Bar2<#[cold] 'a>(PhantomData<&'a ()>); - | ^^^^^^^ -- not a function + | ^^^^^^^ -- not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! @@ -49,11 +49,11 @@ error[E0518]: attribute should be applied to function or closure LL | pub struct Foo3<#[inline] T>(PhantomData); | ^^^^^^^^^ - not a function or closure -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-78957.rs:23:17 | LL | pub struct Bar3<#[cold] T>(PhantomData); - | ^^^^^^^ - not a function + | ^^^^^^^ - not a function definition | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr index 1b58cf9c4ed..b13e418e664 100644 --- a/src/test/ui/macros/issue-68060.stderr +++ b/src/test/ui/macros/issue-68060.stderr @@ -1,11 +1,11 @@ -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/issue-68060.rs:4:13 | LL | #[target_feature(enable = "")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... LL | |_| (), - | ------ not a function + | ------ not a function definition error: aborting due to previous error diff --git a/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.rs b/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.rs index 66156b6e53b..0bf620934ec 100644 --- a/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.rs +++ b/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.rs @@ -1,23 +1,23 @@ #![feature(marker_trait_attr)] -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait struct Struct {} -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait impl Struct {} -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait union Union { x: i32, } -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait const CONST: usize = 10; -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait fn function() {} -#[marker] //~ ERROR attribute can only be applied to a trait +#[marker] //~ ERROR attribute should be applied to a trait type Type = (); fn main() {} diff --git a/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.stderr b/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.stderr index d30b990caac..19a5290dd7e 100644 --- a/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.stderr +++ b/src/test/ui/marker_trait_attr/marker-attribute-on-non-trait.stderr @@ -1,4 +1,4 @@ -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:3:1 | LL | #[marker] @@ -6,7 +6,7 @@ LL | #[marker] LL | struct Struct {} | ---------------- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:6:1 | LL | #[marker] @@ -14,7 +14,7 @@ LL | #[marker] LL | impl Struct {} | -------------- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:9:1 | LL | #[marker] @@ -24,7 +24,7 @@ LL | | x: i32, LL | | } | |_- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:14:1 | LL | #[marker] @@ -32,7 +32,7 @@ LL | #[marker] LL | const CONST: usize = 10; | ------------------------ not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:17:1 | LL | #[marker] @@ -40,7 +40,7 @@ LL | #[marker] LL | fn function() {} | ---------------- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/marker-attribute-on-non-trait.rs:20:1 | LL | #[marker] diff --git a/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.rs b/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.rs index 3c4a09fafd2..143f9a3009b 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.rs @@ -3,11 +3,11 @@ struct Foo; #[non_exhaustive] -//~^ ERROR attribute can only be applied to a struct or enum [E0701] +//~^ ERROR attribute should be applied to a struct or enum [E0701] trait Bar { } #[non_exhaustive] -//~^ ERROR attribute can only be applied to a struct or enum [E0701] +//~^ ERROR attribute should be applied to a struct or enum [E0701] union Baz { f1: u16, f2: u16 diff --git a/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr b/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr index 76d9e2d8205..136cd763b05 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/invalid-attribute.stderr @@ -4,7 +4,7 @@ error: malformed `non_exhaustive` attribute input LL | #[non_exhaustive(anything)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[non_exhaustive]` -error[E0701]: attribute can only be applied to a struct or enum +error[E0701]: attribute should be applied to a struct or enum --> $DIR/invalid-attribute.rs:5:1 | LL | #[non_exhaustive] @@ -13,7 +13,7 @@ LL | LL | trait Bar { } | ------------- not a struct or enum -error[E0701]: attribute can only be applied to a struct or enum +error[E0701]: attribute should be applied to a struct or enum --> $DIR/invalid-attribute.rs:9:1 | LL | #[non_exhaustive] diff --git a/src/test/ui/rfc-2091-track-caller/only-for-fns.rs b/src/test/ui/rfc-2091-track-caller/only-for-fns.rs index bc0ca955280..2d2b01b6f94 100644 --- a/src/test/ui/rfc-2091-track-caller/only-for-fns.rs +++ b/src/test/ui/rfc-2091-track-caller/only-for-fns.rs @@ -1,5 +1,5 @@ #[track_caller] struct S; -//~^^ ERROR attribute should be applied to function +//~^^ ERROR attribute should be applied to a function definition fn main() {} diff --git a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr index 6666dcfa6e5..b36597bded9 100644 --- a/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr +++ b/src/test/ui/rfc-2091-track-caller/only-for-fns.stderr @@ -1,10 +1,10 @@ -error[E0739]: attribute should be applied to function +error[E0739]: attribute should be applied to a function definition --> $DIR/only-for-fns.rs:1:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ LL | struct S; - | --------- not a function + | --------- not a function definition error: aborting due to previous error diff --git a/src/test/ui/rustdoc/doc_keyword.rs b/src/test/ui/rustdoc/doc_keyword.rs index 43b84e5018c..68a8802b2f6 100644 --- a/src/test/ui/rustdoc/doc_keyword.rs +++ b/src/test/ui/rustdoc/doc_keyword.rs @@ -15,6 +15,6 @@ fn foo() {} // Regression test for the ICE described in #83512. trait Foo { #[doc(keyword = "match")] - //~^ ERROR: `#[doc(keyword = "...")]` can only be used on modules + //~^ ERROR: `#[doc(keyword = "...")]` should be used on modules fn quux() {} } diff --git a/src/test/ui/rustdoc/doc_keyword.stderr b/src/test/ui/rustdoc/doc_keyword.stderr index 6ba7034d541..a1d0e4ffc09 100644 --- a/src/test/ui/rustdoc/doc_keyword.stderr +++ b/src/test/ui/rustdoc/doc_keyword.stderr @@ -1,16 +1,16 @@ -error: `#[doc(keyword = "...")]` can only be used on empty modules +error: `#[doc(keyword = "...")]` should be used on empty modules --> $DIR/doc_keyword.rs:6:7 | LL | #[doc(keyword = "hell")] | ^^^^^^^^^^^^^^^^ -error: `#[doc(keyword = "...")]` can only be used on modules +error: `#[doc(keyword = "...")]` should be used on modules --> $DIR/doc_keyword.rs:11:7 | LL | #[doc(keyword = "hall")] | ^^^^^^^^^^^^^^^^ -error: `#[doc(keyword = "...")]` can only be used on modules +error: `#[doc(keyword = "...")]` should be used on modules --> $DIR/doc_keyword.rs:17:11 | LL | #[doc(keyword = "match")] diff --git a/src/test/ui/target-feature/invalid-attribute.stderr b/src/test/ui/target-feature/invalid-attribute.stderr index 25a2c1975e7..889ced9752b 100644 --- a/src/test/ui/target-feature/invalid-attribute.stderr +++ b/src/test/ui/target-feature/invalid-attribute.stderr @@ -34,43 +34,43 @@ LL | fn bar() {} = note: see issue #69098 for more information = help: add `#![feature(target_feature_11)]` to the crate attributes to enable -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:34:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | mod another {} - | -------------- not a function + | -------------- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:39:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | const FOO: usize = 7; - | --------------------- not a function + | --------------------- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:44:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | struct Foo; - | ----------- not a function + | ----------- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:49:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | enum Bar {} - | ----------- not a function + | ----------- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:54:1 | LL | #[target_feature(enable = "sse2")] @@ -81,16 +81,16 @@ LL | | LL | | f1: u16, LL | | f2: u16, LL | | } - | |_- not a function + | |_- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:62:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | trait Baz {} - | ------------ not a function + | ------------ not a function definition error: cannot use `#[inline(always)]` with `#[target_feature]` --> $DIR/invalid-attribute.rs:67:1 @@ -98,7 +98,7 @@ error: cannot use `#[inline(always)]` with `#[target_feature]` LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:85:5 | LL | #[target_feature(enable = "sse2")] @@ -108,16 +108,16 @@ LL | / unsafe { LL | | foo(); LL | | bar(); LL | | } - | |_____- not a function + | |_____- not a function definition -error: attribute should be applied to a function +error: attribute should be applied to a function definition --> $DIR/invalid-attribute.rs:93:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | || {}; - | ----- not a function + | ----- not a function definition error[E0658]: `#[target_feature(..)]` can only be applied to `unsafe` functions --> $DIR/invalid-attribute.rs:77:5 diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs index d9de6d5edb9..1f896da94db 100644 --- a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs +++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.rs @@ -36,11 +36,11 @@ trait Tr5 { } #[rustc_must_implement_one_of(abc, xyz)] -//~^ attribute can only be applied to a trait +//~^ attribute should be applied to a trait fn function() {} #[rustc_must_implement_one_of(abc, xyz)] -//~^ attribute can only be applied to a trait +//~^ attribute should be applied to a trait struct Struct {} fn main() {} diff --git a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr index bc28dc2c4f4..869184f0d1a 100644 --- a/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr +++ b/src/test/ui/traits/default-method/rustc_must_implement_one_of_misuse.stderr @@ -4,7 +4,7 @@ error: malformed `rustc_must_implement_one_of` attribute input LL | #[rustc_must_implement_one_of] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_must_implement_one_of(function1, function2, ...)]` -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/rustc_must_implement_one_of_misuse.rs:38:1 | LL | #[rustc_must_implement_one_of(abc, xyz)] @@ -13,7 +13,7 @@ LL | LL | fn function() {} | ---------------- not a trait -error: attribute can only be applied to a trait +error: attribute should be applied to a trait --> $DIR/rustc_must_implement_one_of_misuse.rs:42:1 | LL | #[rustc_must_implement_one_of(abc, xyz)] From 84a444a1f45ce5a436e19391ea5a1d9312bed1ad Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 22 Jun 2022 15:28:28 +0000 Subject: [PATCH 07/30] Introduce opaque type to hidden type projection --- .../src/diagnostics/conflict_errors.rs | 4 +++- compiler/rustc_borrowck/src/diagnostics/mod.rs | 2 ++ .../src/diagnostics/mutability_errors.rs | 1 + compiler/rustc_borrowck/src/lib.rs | 2 ++ compiler/rustc_borrowck/src/places_conflict.rs | 13 +++++++++++++ compiler/rustc_borrowck/src/prefixes.rs | 1 + compiler/rustc_borrowck/src/type_check/mod.rs | 9 ++++++++- compiler/rustc_codegen_cranelift/src/base.rs | 1 + .../src/value_and_place.rs | 8 ++++++++ compiler/rustc_codegen_ssa/src/mir/place.rs | 16 ++++++++++++++++ .../rustc_const_eval/src/interpret/projection.rs | 10 ++++++++++ .../src/transform/check_consts/check.rs | 1 + .../src/transform/check_consts/qualifs.rs | 1 + .../src/transform/promote_consts.rs | 2 +- compiler/rustc_middle/src/mir/mod.rs | 8 +++++++- compiler/rustc_middle/src/mir/syntax.rs | 7 +++++++ compiler/rustc_middle/src/mir/tcx.rs | 4 +++- compiler/rustc_middle/src/mir/type_foldable.rs | 1 + compiler/rustc_middle/src/mir/visit.rs | 7 ++++++- .../rustc_mir_build/src/build/expr/as_place.rs | 2 ++ .../src/move_paths/abs_domain.rs | 1 + compiler/rustc_mir_transform/src/add_retag.rs | 1 + .../clippy_utils/src/qualify_min_const_fn.rs | 1 + 23 files changed, 97 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 0ceb63477c8..f4d8039eda0 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2093,7 +2093,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } StorageDeadOrDrop::Destructor(_) => kind, }, - ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { + ProjectionElem::OpaqueCast { .. } + | ProjectionElem::Field(..) + | ProjectionElem::Downcast(..) => { match place_ty.ty.kind() { ty::Adt(def, _) if def.has_dtor(tcx) => { // Report the outermost adt with a destructor diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 53c07a3d481..fada3d45fbe 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -226,6 +226,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } ProjectionElem::Downcast(..) if including_downcast.0 => return None, ProjectionElem::Downcast(..) => (), + ProjectionElem::OpaqueCast(..) => (), ProjectionElem::Field(field, _ty) => { // FIXME(project-rfc_2229#36): print capture precisely here. if let Some(field) = self.is_upvar_field_projection(PlaceRef { @@ -286,6 +287,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx) } ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx), + ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(*ty), ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type), }, }; diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 8134e122662..cb7077fe621 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -169,6 +169,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .., ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..), ], diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index e8673ecd3a0..74321ddcd99 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1788,6 +1788,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { for (place_base, elem) in place.iter_projections().rev() { match elem { ProjectionElem::Index(_/*operand*/) | + ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | // assigning to P[i] requires P to be valid. ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) => @@ -2179,6 +2180,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Downcast(..) => { let upvar_field_projection = self.is_upvar_field_projection(place); if let Some(field) = upvar_field_projection { diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index 97335fd0dff..5b67e6aa1cf 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -255,6 +255,7 @@ fn place_components_conflict<'tcx>( | (ProjectionElem::Index { .. }, _, _) | (ProjectionElem::ConstantIndex { .. }, _, _) | (ProjectionElem::Subslice { .. }, _, _) + | (ProjectionElem::OpaqueCast { .. }, _, _) | (ProjectionElem::Downcast { .. }, _, _) => { // Recursive case. This can still be disjoint on a // further iteration if this a shallow access and @@ -322,6 +323,17 @@ fn place_projection_conflict<'tcx>( debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF"); Overlap::EqualOrDisjoint } + (ProjectionElem::OpaqueCast(v1), ProjectionElem::OpaqueCast(v2)) => { + if v1 == v2 { + // same type - recur. + debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE"); + Overlap::EqualOrDisjoint + } else { + // Different types. Disjoint! + debug!("place_element_conflict: DISJOINT-OPAQUE"); + Overlap::Disjoint + } + } (ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => { if f1 == f2 { // same field (e.g., `a.y` vs. `a.y`) - recur. @@ -525,6 +537,7 @@ fn place_projection_conflict<'tcx>( | ProjectionElem::Field(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(..), _, diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index bdf2becb711..2b50cbac9a0 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -81,6 +81,7 @@ impl<'cx, 'tcx> Iterator for Prefixes<'cx, 'tcx> { } ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast { .. } | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { cursor = cursor_base; diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index cf2140097e6..e139a73efc5 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -790,6 +790,11 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } PlaceTy::from_ty(fty) } + ProjectionElem::OpaqueCast(ty) => { + let ty = self.sanitize_type(place, ty); + let ty = self.cx.normalize(ty, location); + PlaceTy::from_ty(ty) + } } } @@ -1195,10 +1200,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { tcx, self.param_env, proj, - |this, field, ()| { + |this, field, _| { let ty = this.field_ty(tcx, field); self.normalize(ty, locations) }, + |_, _| unreachable!(), ); curr_projected_ty = projected_ty; } @@ -2493,6 +2499,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } ProjectionElem::Field(..) | ProjectionElem::Downcast(..) + | ProjectionElem::OpaqueCast(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 63cd4d6de4c..54652623d94 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -825,6 +825,7 @@ pub(crate) fn codegen_place<'tcx>( cplace = cplace.place_deref(fx); } } + PlaceElem::OpaqueCast(ty) => cplace = cplace.place_opaque_cast(fx, ty), PlaceElem::Field(field, _ty) => { cplace = cplace.place_field(fx, field); } diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index a68225de58b..8ff35d2f76d 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -615,6 +615,14 @@ impl<'tcx> CPlace<'tcx> { } } + pub(crate) fn place_opaque_cast( + self, + fx: &mut FunctionCx<'_, '_, 'tcx>, + ty: Ty<'tcx>, + ) -> CPlace<'tcx> { + CPlace { inner: self.inner, layout: fx.layout_of(ty) } + } + pub(crate) fn place_field( self, fx: &mut FunctionCx<'_, '_, 'tcx>, diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 58cee0c8bb0..421d6f807ae 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -411,6 +411,21 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { downcast } + pub fn project_type>( + &self, + bx: &mut Bx, + ty: Ty<'tcx>, + ) -> Self { + let mut downcast = *self; + downcast.layout = bx.cx().layout_of(ty); + + // Cast to the appropriate type. + let variant_ty = bx.cx().backend_type(downcast.layout); + downcast.llval = bx.pointercast(downcast.llval, bx.cx().type_ptr_to(variant_ty)); + + downcast + } + pub fn storage_live>(&self, bx: &mut Bx) { bx.lifetime_start(self.llval, self.layout.size); } @@ -459,6 +474,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::ProjectionElem::Field(ref field, _) => { cg_base.project_field(bx, field.index()) } + mir::ProjectionElem::OpaqueCast(ty) => cg_base.project_type(bx, ty), mir::ProjectionElem::Index(index) => { let index = &mir::Operand::Copy(mir::Place::from(index)); let index = self.codegen_operand(bx, index); diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 31fb6a8944d..eced1e9e639 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -351,6 +351,11 @@ where ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { + OpaqueCast(ty) => { + let mut place = *base; + place.layout = self.layout_of(ty)?; + place + } Field(field, _) => self.place_field(base, field.index())?, Downcast(_, variant) => self.place_downcast(base, variant)?, Deref => self.deref_operand(&self.place_to_op(base)?)?.into(), @@ -375,6 +380,11 @@ where ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { use rustc_middle::mir::ProjectionElem::*; Ok(match proj_elem { + OpaqueCast(ty) => { + let mut op = *base; + op.layout = self.layout_of(ty)?; + op + } Field(field, _) => self.operand_field(base, field.index())?, Downcast(_, variant) => self.operand_downcast(base, variant)?, Deref => self.deref_operand(base)?.into(), diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 628298df473..0581f491978 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -652,6 +652,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) + | ProjectionElem::OpaqueCast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {} diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs index 29464cf8c4e..c2b4f6eca5c 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs @@ -316,6 +316,7 @@ where ProjectionElem::Deref | ProjectionElem::Field(_, _) + | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | ProjectionElem::Downcast(_, _) diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index ed4d8c95d1e..daa154576ae 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -361,7 +361,7 @@ impl<'tcx> Validator<'_, 'tcx> { return Err(Unpromotable); } } - ProjectionElem::Downcast(..) => { + ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => { return Err(Unpromotable); } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index f61cb7e8c47..0b5d23be58d 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1397,6 +1397,7 @@ impl ProjectionElem { Self::Field(_, _) | Self::Index(_) + | Self::OpaqueCast(_) | Self::ConstantIndex { .. } | Self::Subslice { .. } | Self::Downcast(_, _) => false, @@ -1574,7 +1575,9 @@ impl Debug for Place<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { for elem in self.projection.iter().rev() { match elem { - ProjectionElem::Downcast(_, _) | ProjectionElem::Field(_, _) => { + ProjectionElem::OpaqueCast(_) + | ProjectionElem::Downcast(_, _) + | ProjectionElem::Field(_, _) => { write!(fmt, "(").unwrap(); } ProjectionElem::Deref => { @@ -1590,6 +1593,9 @@ impl Debug for Place<'_> { for elem in self.projection.iter() { match elem { + ProjectionElem::OpaqueCast(ty) => { + write!(fmt, " as {})", ty)?; + } ProjectionElem::Downcast(Some(name), _index) => { write!(fmt, " as {})", name)?; } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 510316c778b..263c2ca3c70 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -754,6 +754,9 @@ pub type AssertMessage<'tcx> = AssertKind>; /// generator has more than one variant, the parent place's variant index must be set, indicating /// which variant is being used. If it has just one variant, the variant index may or may not be /// included - the single possible variant is inferred if it is not included. +/// - [`OpaqueCast`](ProjectionElem::OpaqueCast): This projection changes the place's type to the +/// given one, and makes no other changes. A `OpaqueCast` projection on any type other than an +/// opaque type from the current crate is not well-formed. /// - [`ConstantIndex`](ProjectionElem::ConstantIndex): Computes an offset in units of `T` into the /// place as described in the documentation for the `ProjectionElem`. The resulting address is /// the parent's address plus that offset, and the type is `T`. This is only legal if the parent @@ -856,6 +859,10 @@ pub enum ProjectionElem { /// /// The included Symbol is the name of the variant, used for printing MIR. Downcast(Option, VariantIdx), + + /// Like an explicit cast from an opaque type to a concrete type, but without + /// requiring an intermediate variable. + OpaqueCast(T), } /// Alias for projections as they appear in places, where the base is a place diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index fd3359ea80f..c6975df45ef 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -57,7 +57,7 @@ impl<'tcx> PlaceTy<'tcx> { /// `PlaceElem`, where we can just use the `Ty` that is already /// stored inline on field projection elems. pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> { - self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty) + self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty, |_, ty| ty) } /// `place_ty.projection_ty_core(tcx, elem, |...| { ... })` @@ -71,6 +71,7 @@ impl<'tcx> PlaceTy<'tcx> { param_env: ty::ParamEnv<'tcx>, elem: &ProjectionElem, mut handle_field: impl FnMut(&Self, Field, T) -> Ty<'tcx>, + mut handle_opaque_cast: impl FnMut(&Self, T) -> Ty<'tcx>, ) -> PlaceTy<'tcx> where V: ::std::fmt::Debug, @@ -109,6 +110,7 @@ impl<'tcx> PlaceTy<'tcx> { PlaceTy { ty: self.ty, variant_index: Some(index) } } ProjectionElem::Field(f, fty) => PlaceTy::from_ty(handle_field(&self, f, fty)), + ProjectionElem::OpaqueCast(ty) => PlaceTy::from_ty(handle_opaque_cast(&self, ty)), }; debug!("projection_ty self: {:?} elem: {:?} yields: {:?}", self, elem, answer); answer diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index 82a6b0c506f..a73ef23e281 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -182,6 +182,7 @@ impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> { Ok(match self { Deref => Deref, Field(f, ty) => Field(f, ty.try_fold_with(folder)?), + OpaqueCast(ty) => OpaqueCast(ty.try_fold_with(folder)?), Index(v) => Index(v.try_fold_with(folder)?), Downcast(symbol, variantidx) => Downcast(symbol, variantidx), ConstantIndex { offset, min_length, from_end } => { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index d285728ec07..e5599fb15ad 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1064,6 +1064,11 @@ macro_rules! visit_place_fns { self.visit_ty(&mut new_ty, TyContext::Location(location)); if ty != new_ty { Some(PlaceElem::Field(field, new_ty)) } else { None } } + PlaceElem::OpaqueCast(ty) => { + let mut new_ty = ty; + self.visit_ty(&mut new_ty, TyContext::Location(location)); + if ty != new_ty { Some(PlaceElem::OpaqueCast(new_ty)) } else { None } + } PlaceElem::Deref | PlaceElem::ConstantIndex { .. } | PlaceElem::Subslice { .. } @@ -1133,7 +1138,7 @@ macro_rules! visit_place_fns { location: Location, ) { match elem { - ProjectionElem::Field(_field, ty) => { + ProjectionElem::OpaqueCast(ty) | ProjectionElem::Field(_, ty) => { self.visit_ty(ty, TyContext::Location(location)); } ProjectionElem::Index(local) => { diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index e88f9dc1f08..82debdf7fa1 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -105,6 +105,7 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( continue; } ProjectionElem::Index(..) + | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { // We don't capture array-access projections. @@ -795,6 +796,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ProjectionElem::Field(..) | ProjectionElem::Downcast(..) + | ProjectionElem::OpaqueCast(..) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => (), } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs index 28936274baa..7806e8f45d3 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs @@ -48,6 +48,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> { match *self { ProjectionElem::Deref => ProjectionElem::Deref, ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty.lift()), + ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty.lift()), ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()), ProjectionElem::Subslice { from, to, from_end } => { ProjectionElem::Subslice { from, to, from_end } diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index b91ae083cf5..86327ade94b 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -28,6 +28,7 @@ fn is_stable(place: PlaceRef<'_>) -> bool { ProjectionElem::Field { .. } | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } | + ProjectionElem::OpaqueCast { .. } | ProjectionElem::Downcast { .. } => true, } }) diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 3bf75bcbee8..9690ad27771 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -252,6 +252,7 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B } }, ProjectionElem::ConstantIndex { .. } + | ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Deref From c4cb043f063fb75d2278ebedeb13feaa94dc8c95 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 14 Jul 2022 20:32:45 -0400 Subject: [PATCH 08/30] interpret/visitor: support visiting with a PlaceTy --- .../src/const_eval/valtrees.rs | 2 +- .../rustc_const_eval/src/interpret/operand.rs | 14 +- .../rustc_const_eval/src/interpret/place.rs | 16 +- .../src/interpret/projection.rs | 12 +- .../rustc_const_eval/src/interpret/visitor.rs | 272 +++++++++++++++--- 5 files changed, 268 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 2288a4e7b6c..8fff4571d12 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -436,7 +436,7 @@ fn valtree_into_mplace<'tcx>( let offset = place_adjusted.layout.fields.offset(i); place - .offset( + .offset_with_meta( offset, MemPlaceMeta::Meta(Scalar::from_machine_usize( num_elems as u64, diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 1465b986293..22dc1e80f13 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -297,7 +297,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { } } - pub fn offset( + pub fn offset_with_meta( &self, offset: Size, meta: MemPlaceMeta, @@ -305,7 +305,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { match self.try_as_mplace() { - Ok(mplace) => Ok(mplace.offset(offset, meta, layout, cx)?.into()), + Ok(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, cx)?.into()), Err(imm) => { assert!( matches!(*imm, Immediate::Uninit), @@ -317,6 +317,16 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> { } } } + + pub fn offset( + &self, + offset: Size, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert!(!layout.is_unsized()); + self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + } } impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 2001359d199..4ee6ed29b1e 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -171,7 +171,7 @@ impl MemPlace { } #[inline] - pub fn offset<'tcx>( + pub fn offset_with_meta<'tcx>( self, offset: Size, meta: MemPlaceMeta, @@ -205,7 +205,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { } #[inline] - pub fn offset( + pub fn offset_with_meta( &self, offset: Size, meta: MemPlaceMeta, @@ -213,12 +213,22 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> { cx: &impl HasDataLayout, ) -> InterpResult<'tcx, Self> { Ok(MPlaceTy { - mplace: self.mplace.offset(offset, meta, cx)?, + mplace: self.mplace.offset_with_meta(offset, meta, cx)?, align: self.align.restrict_for_offset(offset), layout, }) } + pub fn offset( + &self, + offset: Size, + layout: TyAndLayout<'tcx>, + cx: &impl HasDataLayout, + ) -> InterpResult<'tcx, Self> { + assert!(!layout.is_unsized()); + self.offset_with_meta(offset, MemPlaceMeta::None, layout, cx) + } + #[inline] pub fn from_aligned_ptr(ptr: Pointer>, layout: TyAndLayout<'tcx>) -> Self { MPlaceTy { mplace: MemPlace::from_ptr(ptr), layout, align: layout.align.abi } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 31fb6a8944d..22ec276250f 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -63,7 +63,7 @@ where // We do not look at `base.layout.align` nor `field_layout.align`, unlike // codegen -- mostly to see if we can get away with that - base.offset(offset, meta, field_layout, self) + base.offset_with_meta(offset, meta, field_layout, self) } /// Gets the place of a field inside the place, and also the field's type. @@ -193,9 +193,7 @@ where let offset = stride * index; // `Size` multiplication // All fields have the same layout. let field_layout = base.layout.field(self, 0); - assert!(!field_layout.is_unsized()); - - base.offset(offset, MemPlaceMeta::None, field_layout, self) + base.offset(offset, field_layout, self) } _ => span_bug!( self.cur_span(), @@ -215,10 +213,10 @@ where let abi::FieldsShape::Array { stride, .. } = base.layout.fields else { span_bug!(self.cur_span(), "operand_array_fields: expected an array layout"); }; - let layout = base.layout.field(self, 0); + let field_layout = base.layout.field(self, 0); let dl = &self.tcx.data_layout; // `Size` multiplication - Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl))) + Ok((0..len).map(move |i| base.offset(stride * i, field_layout, dl))) } /// Index into an array. @@ -326,7 +324,7 @@ where } }; let layout = self.layout_of(ty)?; - base.offset(from_offset, meta, layout, self) + base.offset_with_meta(from_offset, meta, layout, self) } pub fn place_subslice( diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index c262bca9bb4..f6a0c19d259 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -8,23 +8,33 @@ use rustc_target::abi::{FieldsShape, VariantIdx, Variants}; use std::num::NonZeroUsize; -use super::{InterpCx, MPlaceTy, Machine, OpTy}; +use super::{InterpCx, MPlaceTy, Machine, OpTy, PlaceTy}; -// A thing that we can project into, and that has a layout. -// This wouldn't have to depend on `Machine` but with the current type inference, -// that's just more convenient to work with (avoids repeating all the `Machine` bounds). +/// A thing that we can project into, and that has a layout. +/// This wouldn't have to depend on `Machine` but with the current type inference, +/// that's just more convenient to work with (avoids repeating all the `Machine` bounds). pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { /// Gets this value's layout. fn layout(&self) -> TyAndLayout<'tcx>; - /// Makes this into an `OpTy`. - fn to_op(&self, ecx: &InterpCx<'mir, 'tcx, M>) - -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; + /// Makes this into an `OpTy`, in a cheap way that is good for reading. + fn to_op_for_read( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; + + /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. + fn to_op_for_proj( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + self.to_op_for_read(ecx) + } /// Creates this from an `OpTy`. /// - /// If `to_op` only ever produces `Indirect` operands, then this one is definitely `Indirect`. - fn from_op(mplace: OpTy<'tcx, M::PointerTag>) -> Self; + /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. + fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self; /// Projects to the given enum variant. fn project_downcast( @@ -41,8 +51,50 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { ) -> InterpResult<'tcx, Self>; } -// Operands and memory-places are both values. -// Places in general are not due to `place_field` having to do `force_allocation`. +/// A thing that we can project into given *mutable* access to `ecx`, and that has a layout. +/// This wouldn't have to depend on `Machine` but with the current type inference, +/// that's just more convenient to work with (avoids repeating all the `Machine` bounds). +pub trait ValueMut<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy { + /// Gets this value's layout. + fn layout(&self) -> TyAndLayout<'tcx>; + + /// Makes this into an `OpTy`, in a cheap way that is good for reading. + fn to_op_for_read( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; + + /// Makes this into an `OpTy`, in a potentially more expensive way that is good for projections. + fn to_op_for_proj( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>; + + /// Creates this from an `OpTy`. + /// + /// If `to_op_for_proj` only ever produces `Indirect` operands, then this one is definitely `Indirect`. + fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self; + + /// Projects to the given enum variant. + fn project_downcast( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self>; + + /// Projects to the n-th field. + fn project_field( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self>; +} + +// We cannot have a general impl which shows that Value implies ValueMut. (When we do, it says we +// cannot `impl ValueMut for PlaceTy` because some downstream crate could `impl Value for PlaceTy`.) +// So we have some copy-paste here. (We could have a macro but since we only have 2 types with this +// double-impl, that would barely make the code shorter, if at all.) + impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tcx, M::PointerTag> { #[inline(always)] fn layout(&self) -> TyAndLayout<'tcx> { @@ -50,7 +102,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc } #[inline(always)] - fn to_op( + fn to_op_for_read( &self, _ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { @@ -58,8 +110,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc } #[inline(always)] - fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self { - op + fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + *op } #[inline(always)] @@ -81,6 +133,54 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc } } +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> + for OpTy<'tcx, M::PointerTag> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + #[inline(always)] + fn to_op_for_read( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + Ok(*self) + } + + #[inline(always)] + fn to_op_for_proj( + &self, + _ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + Ok(*self) + } + + #[inline(always)] + fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + *op + } + + #[inline(always)] + fn project_downcast( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self> { + ecx.operand_downcast(self, variant) + } + + #[inline(always)] + fn project_field( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { + ecx.operand_field(self, field) + } +} + impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for MPlaceTy<'tcx, M::PointerTag> { @@ -90,7 +190,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> } #[inline(always)] - fn to_op( + fn to_op_for_read( &self, _ecx: &InterpCx<'mir, 'tcx, M>, ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { @@ -98,8 +198,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> } #[inline(always)] - fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self { - // assert is justified because our `to_op` only ever produces `Indirect` operands. + fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + // assert is justified because our `to_op_for_read` only ever produces `Indirect` operands. op.assert_mem_place() } @@ -122,11 +222,111 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> } } +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> + for MPlaceTy<'tcx, M::PointerTag> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + #[inline(always)] + fn to_op_for_read( + &self, + _ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + Ok(self.into()) + } + + #[inline(always)] + fn to_op_for_proj( + &self, + _ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + Ok(self.into()) + } + + #[inline(always)] + fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + // assert is justified because our `to_op_for_proj` only ever produces `Indirect` operands. + op.assert_mem_place() + } + + #[inline(always)] + fn project_downcast( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self> { + ecx.mplace_downcast(self, variant) + } + + #[inline(always)] + fn project_field( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { + ecx.mplace_field(self, field) + } +} + +impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M> + for PlaceTy<'tcx, M::PointerTag> +{ + #[inline(always)] + fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + #[inline(always)] + fn to_op_for_read( + &self, + ecx: &InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + // We `force_allocation` here so that `from_op` below can work. + ecx.place_to_op(self) + } + + #[inline(always)] + fn to_op_for_proj( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> { + // We `force_allocation` here so that `from_op` below can work. + Ok(ecx.force_allocation(self)?.into()) + } + + #[inline(always)] + fn from_op(op: &OpTy<'tcx, M::PointerTag>) -> Self { + // assert is justified because our `to_op` only ever produces `Indirect` operands. + op.assert_mem_place().into() + } + + #[inline(always)] + fn project_downcast( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + variant: VariantIdx, + ) -> InterpResult<'tcx, Self> { + ecx.place_downcast(self, variant) + } + + #[inline(always)] + fn project_field( + &self, + ecx: &mut InterpCx<'mir, 'tcx, M>, + field: usize, + ) -> InterpResult<'tcx, Self> { + ecx.place_field(self, field) + } +} + macro_rules! make_value_visitor { - ($visitor_trait_name:ident, $($mutability:ident)?) => { + ($visitor_trait:ident, $value_trait:ident, $($mutability:ident)?) => { // How to traverse a value and what to do when we are at the leaves. - pub trait $visitor_trait_name<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { - type V: Value<'mir, 'tcx, M>; + pub trait $visitor_trait<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>: Sized { + type V: $value_trait<'mir, 'tcx, M>; /// The visitor must have an `InterpCx` in it. fn ecx(&$($mutability)? self) @@ -215,19 +415,20 @@ macro_rules! make_value_visitor { } fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx> { - trace!("walk_value: type: {}", v.layout().ty); + let ty = v.layout().ty; + trace!("walk_value: type: {ty}"); // Special treatment for special types, where the (static) layout is not sufficient. - match *v.layout().ty.kind() { + match *ty.kind() { // If it is a trait object, switch to the real type that was used to create it. ty::Dynamic(..) => { // unsized values are never immediate, so we can assert_mem_place - let op = v.to_op(self.ecx())?; + let op = v.to_op_for_read(self.ecx())?; let dest = op.assert_mem_place(); - let inner = self.ecx().unpack_dyn_trait(&dest)?.1; - trace!("walk_value: dyn object layout: {:#?}", inner.layout); + let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.1; + trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout); // recurse with the inner type - return self.visit_field(&v, 0, &Value::from_op(inner.into())); + return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into())); }, // Slices do not need special handling here: they have `Array` field // placement with length 0, so we enter the `Array` case below which @@ -278,10 +479,10 @@ macro_rules! make_value_visitor { // Visit the fields of this value. match v.layout().fields { - FieldsShape::Primitive => {}, + FieldsShape::Primitive => {} FieldsShape::Union(fields) => { self.visit_union(v, fields)?; - }, + } FieldsShape::Arbitrary { ref offsets, .. } => { // FIXME: We collect in a vec because otherwise there are lifetime // errors: Projecting to a field needs access to `ecx`. @@ -291,16 +492,17 @@ macro_rules! make_value_visitor { }) .collect(); self.visit_aggregate(v, fields.into_iter())?; - }, + } FieldsShape::Array { .. } => { - // Let's get an mplace first. - let op = v.to_op(self.ecx())?; + // Let's get an mplace (or immediate) first. + // This might `force_allocate` if `v` is a `PlaceTy`, but `place_index` does that anyway. + let op = v.to_op_for_proj(self.ecx())?; // Now we can go over all the fields. // This uses the *run-time length*, i.e., if we are a slice, // the dynamic info from the metadata is used. let iter = self.ecx().operand_array_fields(&op)? .map(|f| f.and_then(|f| { - Ok(Value::from_op(f)) + Ok($value_trait::from_op(&f)) })); self.visit_aggregate(v, iter)?; } @@ -310,7 +512,7 @@ macro_rules! make_value_visitor { // If this is a multi-variant layout, find the right variant and proceed // with *its* fields. Variants::Multiple { .. } => { - let op = v.to_op(self.ecx())?; + let op = v.to_op_for_read(self.ecx())?; let idx = self.read_discriminant(&op)?; let inner = v.project_downcast(self.ecx(), idx)?; trace!("walk_value: variant layout: {:#?}", inner.layout()); @@ -325,5 +527,5 @@ macro_rules! make_value_visitor { } } -make_value_visitor!(ValueVisitor,); -make_value_visitor!(MutValueVisitor, mut); +make_value_visitor!(ValueVisitor, Value,); +make_value_visitor!(MutValueVisitor, ValueMut, mut); From a034446faea140b00e59ada55519ac9bc7b08ee9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 23 Jun 2022 14:32:01 +0000 Subject: [PATCH 09/30] Add some tracing instrumentation --- .../rustc_mir_build/src/build/expr/as_temp.rs | 5 +-- .../rustc_mir_build/src/build/expr/into.rs | 3 +- .../rustc_mir_build/src/build/matches/mod.rs | 35 +++++++++---------- .../src/build/matches/simplify.rs | 3 +- .../rustc_mir_build/src/build/matches/test.rs | 1 + compiler/rustc_mir_build/src/build/scope.rs | 4 +-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 4 +++ .../rustc_mir_build/src/thir/pattern/mod.rs | 1 + 8 files changed, 29 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs index 724b72f8769..dac8862647a 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -23,6 +23,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability)) } + #[instrument(skip(self), level = "debug")] fn as_temp_inner( &mut self, mut block: BasicBlock, @@ -30,10 +31,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, mutability: Mutability, ) -> BlockAnd { - debug!( - "as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})", - block, temp_lifetime, expr, mutability - ); let this = self; let expr_span = expr.span; diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 017d43d10a9..6132de6d486 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -15,14 +15,13 @@ use std::iter; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, storing the result into `destination`, which /// is assumed to be uninitialized. + #[instrument(level = "debug", skip(self))] pub(crate) fn expr_into_dest( &mut self, destination: Place<'tcx>, mut block: BasicBlock, expr: &Expr<'tcx>, ) -> BlockAnd<()> { - debug!("expr_into_dest(destination={:?}, block={:?}, expr={:?})", destination, block, expr); - // since we frequently have to reference `self` from within a // closure, where `self` would be shadowed, it's easier to // just use the name `this` uniformly diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 7067a48b783..f2c8b80b1d7 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -654,6 +654,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// scope for the bindings in these patterns, if such a scope had to be /// created. NOTE: Declaring the bindings should always be done in their /// drop scope. + #[instrument(skip(self), level = "debug")] pub(crate) fn declare_bindings( &mut self, mut visibility_scope: Option, @@ -662,7 +663,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { has_guard: ArmHasGuard, opt_match_place: Option<(Option<&Place<'tcx>>, Span)>, ) -> Option { - debug!("declare_bindings: pattern={:?}", pattern); self.visit_primary_bindings( &pattern, UserTypeProjections::none(), @@ -1048,6 +1048,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// if `x.0` matches `false` (for the third arm). In the (impossible at /// runtime) case when `x.0` is now `true`, we branch to /// `otherwise_block`. + #[instrument(skip(self, fake_borrows), level = "debug")] fn match_candidates<'pat>( &mut self, span: Span, @@ -1057,11 +1058,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidates: &mut [&mut Candidate<'pat, 'tcx>], fake_borrows: &mut Option>>, ) { - debug!( - "matched_candidate(span={:?}, candidates={:?}, start_block={:?}, otherwise_block={:?})", - span, candidates, start_block, otherwise_block, - ); - // Start by simplifying candidates. Once this process is complete, all // the match pairs which remain require some form of test, whether it // be a switch or pattern comparison. @@ -1380,6 +1376,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) } + #[instrument( + skip(self, otherwise, or_span, place, fake_borrows, candidate, pats), + level = "debug" + )] fn test_or_pattern<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, @@ -1389,7 +1389,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { place: PlaceBuilder<'tcx>, fake_borrows: &mut Option>>, ) { - debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); + debug!("candidate={:#?}\npats={:#?}", candidate, pats); let mut or_candidates: Vec<_> = pats .iter() .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard)) @@ -1634,9 +1634,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidates = rest; } // at least the first candidate ought to be tested - assert!(total_candidate_count > candidates.len()); - debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len()); - debug!("test_candidates: untested_candidates: {}", candidates.len()); + assert!( + total_candidate_count > candidates.len(), + "{}, {:#?}", + total_candidate_count, + candidates + ); + debug!("tested_candidates: {}", total_candidate_count - candidates.len()); + debug!("untested_candidates: {}", candidates.len()); // HACK(matthewjasper) This is a closure so that we can let the test // create its blocks before the rest of the match. This currently @@ -2195,6 +2200,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// first local is a binding for occurrences of `var` in the guard, which /// will have type `&T`. The second local is a binding for occurrences of /// `var` in the arm body, which will have type `T`. + #[instrument(skip(self), level = "debug")] fn declare_binding( &mut self, source_info: SourceInfo, @@ -2209,19 +2215,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { opt_match_place: Option<(Option>, Span)>, pat_span: Span, ) { - debug!( - "declare_binding(var_id={:?}, name={:?}, mode={:?}, var_ty={:?}, \ - visibility_scope={:?}, source_info={:?})", - var_id, name, mode, var_ty, visibility_scope, source_info - ); - let tcx = self.tcx; let debug_source_info = SourceInfo { span: source_info.span, scope: visibility_scope }; let binding_mode = match mode { BindingMode::ByValue => ty::BindingMode::BindByValue(mutability), BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability), }; - debug!("declare_binding: user_ty={:?}", user_ty); let local = LocalDecl::<'tcx> { mutability, ty: var_ty, @@ -2271,7 +2270,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { LocalsForNode::One(for_arm_body) }; - debug!("declare_binding: vars={:?}", locals); + debug!(?locals); self.var_indices.insert(var_id, locals); } diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index c6298904140..878b17ce5a4 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -37,12 +37,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// only generates a single switch. If this happens this method returns /// `true`. + #[instrument(skip(self, candidate), level = "debug")] pub(super) fn simplify_candidate<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, ) -> bool { // repeatedly simplify match pairs until fixed point is reached - debug!(?candidate, "simplify_candidate"); + debug!("{:#?}", candidate); // existing_bindings and new_bindings exists to keep the semantics in order. // Reversing the binding order for bindings after `@` changes the binding order in places diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 598da80c574..1ef2524f136 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -144,6 +144,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + #[instrument(skip(self, make_target_blocks, place_builder), level = "debug")] pub(super) fn perform_test( &mut self, match_start_span: Span, diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index b9fd8c50e6a..ab37c480285 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -553,6 +553,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Convenience wrapper that pushes a scope and then executes `f` /// to build its contents, popping the scope afterwards. + #[instrument(skip(self, f), level = "debug")] pub(crate) fn in_scope( &mut self, region_scope: (region::Scope, SourceInfo), @@ -562,7 +563,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { where F: FnOnce(&mut Builder<'a, 'tcx>) -> BlockAnd, { - debug!("in_scope(region_scope={:?})", region_scope); let source_scope = self.source_scope; let tcx = self.tcx; if let LintLevel::Explicit(current_hir_id) = lint_level { @@ -589,7 +589,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rv = unpack!(block = f(self)); unpack!(block = self.pop_scope(region_scope, block)); self.source_scope = source_scope; - debug!("in_scope: exiting region_scope={:?} block={:?}", region_scope, block); + debug!(?block); block.and(rv) } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 4eb3607e9cc..05da33caa91 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -48,6 +48,8 @@ impl<'tcx> Cx<'tcx> { _ => None, }; + trace!(?expr.ty); + // Now apply adjustments, if any. for adjustment in self.typeck_results.expr_adjustments(hir_expr) { trace!(?expr, ?adjustment); @@ -56,6 +58,8 @@ impl<'tcx> Cx<'tcx> { self.apply_adjustment(hir_expr, expr, adjustment, adjustment_span.unwrap_or(span)); } + trace!(?expr.ty, "after adjustments"); + // Next, wrap this up in the expr's scope. expr = Expr { temp_lifetime, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index a13748a2d47..7d22f7b69d8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -196,6 +196,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } } + #[instrument(skip(self), level = "debug")] fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Pat<'tcx> { let mut ty = self.typeck_results.node_type(pat.hir_id); From eeb10335ce94d35f3c13f7d14fabc91b3dcd469c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 23 Jun 2022 16:08:02 +0000 Subject: [PATCH 10/30] Pass one argument instead of fetching two fields of it at every call site --- .../src/build/expr/as_place.rs | 57 +++++++------------ .../src/build/expr/as_rvalue.rs | 12 ++-- .../rustc_mir_build/src/build/expr/into.rs | 4 +- .../rustc_mir_build/src/build/matches/mod.rs | 43 ++++++-------- .../src/build/matches/simplify.rs | 12 ++-- .../rustc_mir_build/src/build/matches/test.rs | 6 +- .../rustc_mir_build/src/build/matches/util.rs | 26 ++++----- 7 files changed, 60 insertions(+), 100 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 82debdf7fa1..98502737dc5 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -71,7 +71,7 @@ pub(crate) enum PlaceBase { /// This is used internally when building a place for an expression like `a.b.c`. The fields `b` /// and `c` can be progressively pushed onto the place builder that is created when converting `a`. #[derive(Clone, Debug, PartialEq)] -pub(crate) struct PlaceBuilder<'tcx> { +pub(in crate::build) struct PlaceBuilder<'tcx> { base: PlaceBase, projection: Vec>, } @@ -202,10 +202,9 @@ fn find_capture_matching_projections<'a, 'tcx>( /// `PlaceBuilder` now starts from `PlaceBase::Local`. /// /// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found. -fn to_upvars_resolved_place_builder<'a, 'tcx>( +fn to_upvars_resolved_place_builder<'tcx>( from_builder: PlaceBuilder<'tcx>, - tcx: TyCtxt<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, + cx: &Builder<'_, 'tcx>, ) -> Result, PlaceBuilder<'tcx>> { match from_builder.base { PlaceBase::Local(_) => Ok(from_builder), @@ -220,13 +219,13 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( let Some((capture_index, capture)) = find_capture_matching_projections( - typeck_results, + cx.typeck_results, var_hir_id, closure_def_id, &from_builder.projection, ) else { - let closure_span = tcx.def_span(closure_def_id); - if !enable_precise_capture(tcx, closure_span) { + let closure_span = cx.tcx.def_span(closure_def_id); + if !enable_precise_capture(cx.tcx, closure_span) { bug!( "No associated capture found for {:?}[{:#?}] even though \ capture_disjoint_fields isn't enabled", @@ -243,8 +242,8 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>( }; // We won't be building MIR if the closure wasn't local - let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); - let closure_ty = typeck_results.node_type(closure_hir_id); + let closure_hir_id = cx.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); + let closure_ty = cx.typeck_results.node_type(closure_hir_id); let substs = match closure_ty.kind() { ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs), @@ -316,24 +315,16 @@ fn strip_prefix<'tcx>( } impl<'tcx> PlaceBuilder<'tcx> { - pub(crate) fn into_place<'a>( - self, - tcx: TyCtxt<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - ) -> Place<'tcx> { + pub(crate) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> { if let PlaceBase::Local(local) = self.base { - Place { local, projection: tcx.intern_place_elems(&self.projection) } + Place { local, projection: cx.tcx.intern_place_elems(&self.projection) } } else { - self.expect_upvars_resolved(tcx, typeck_results).into_place(tcx, typeck_results) + self.expect_upvars_resolved(cx).into_place(cx) } } - fn expect_upvars_resolved<'a>( - self, - tcx: TyCtxt<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - ) -> PlaceBuilder<'tcx> { - to_upvars_resolved_place_builder(self, tcx, typeck_results).unwrap() + fn expect_upvars_resolved(self, cx: &Builder<'_, 'tcx>) -> PlaceBuilder<'tcx> { + to_upvars_resolved_place_builder(self, cx).unwrap() } /// Attempts to resolve the `PlaceBuilder`. @@ -347,12 +338,11 @@ impl<'tcx> PlaceBuilder<'tcx> { /// not captured. This can happen because the final mir that will be /// generated doesn't require a read for this place. Failures will only /// happen inside closures. - pub(crate) fn try_upvars_resolved<'a>( + pub(crate) fn try_upvars_resolved( self, - tcx: TyCtxt<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, + cx: &Builder<'_, 'tcx>, ) -> Result, PlaceBuilder<'tcx>> { - to_upvars_resolved_place_builder(self, tcx, typeck_results) + to_upvars_resolved_place_builder(self, cx) } pub(crate) fn base(&self) -> PlaceBase { @@ -412,7 +402,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, ) -> BlockAnd> { let place_builder = unpack!(block = self.as_place_builder(block, expr)); - block.and(place_builder.into_place(self.tcx, self.typeck_results)) + block.and(place_builder.into_place(self)) } /// This is used when constructing a compound `Place`, so that we can avoid creating @@ -436,7 +426,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr: &Expr<'tcx>, ) -> BlockAnd> { let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr)); - block.and(place_builder.into_place(self.tcx, self.typeck_results)) + block.and(place_builder.into_place(self)) } /// This is used when constructing a compound `Place`, so that we can avoid creating @@ -531,7 +521,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { inferred_ty: expr.ty, }); - let place = place_builder.clone().into_place(this.tcx, this.typeck_results); + let place = place_builder.clone().into_place(this); this.cfg.push( block, Statement { @@ -683,7 +673,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if is_outermost_index { self.read_fake_borrows(block, fake_borrow_temps, source_info) } else { - base_place = base_place.expect_upvars_resolved(self.tcx, self.typeck_results); + base_place = base_place.expect_upvars_resolved(self); self.add_fake_borrows_of_base( &base_place, block, @@ -711,12 +701,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let lt = self.temp(bool_ty, expr_span); // len = len(slice) - self.cfg.push_assign( - block, - source_info, - len, - Rvalue::Len(slice.into_place(self.tcx, self.typeck_results)), - ); + self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.into_place(self))); // lt = idx < len self.cfg.push_assign( block, diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index 15f2d17c4e0..93f76333165 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -321,11 +321,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let place_builder = unpack!(block = this.as_place_builder(block, &this.thir[*thir_place])); - if let Ok(place_builder_resolved) = - place_builder.try_upvars_resolved(this.tcx, this.typeck_results) - { - let mir_place = - place_builder_resolved.into_place(this.tcx, this.typeck_results); + if let Ok(place_builder_resolved) = place_builder.try_upvars_resolved(this) { + let mir_place = place_builder_resolved.into_place(this); this.cfg.push_fake_read( block, this.source_info(this.tcx.hir().span(*hir_id)), @@ -616,8 +613,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // by the parent itself. The mutability of the current capture // is same as that of the capture in the parent closure. PlaceBase::Upvar { .. } => { - let enclosing_upvars_resolved = - arg_place_builder.clone().into_place(this.tcx, this.typeck_results); + let enclosing_upvars_resolved = arg_place_builder.clone().into_place(this); match enclosing_upvars_resolved.as_ref() { PlaceRef { @@ -654,7 +650,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false }, }; - let arg_place = arg_place_builder.into_place(this.tcx, this.typeck_results); + let arg_place = arg_place_builder.into_place(this); this.cfg.push_assign( block, diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 6132de6d486..d1ef515b3d2 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -365,9 +365,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None => { let place_builder = place_builder.clone(); this.consume_by_copy_or_move( - place_builder - .field(n, *ty) - .into_place(this.tcx, this.typeck_results), + place_builder.field(n, *ty).into_place(this), ) } }) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index f2c8b80b1d7..7fc28d624da 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -220,10 +220,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let cause_matched_place = FakeReadCause::ForMatchedPlace(None); let source_info = self.source_info(scrutinee_span); - if let Ok(scrutinee_builder) = - scrutinee_place_builder.clone().try_upvars_resolved(self.tcx, self.typeck_results) - { - let scrutinee_place = scrutinee_builder.into_place(self.tcx, self.typeck_results); + if let Ok(scrutinee_builder) = scrutinee_place_builder.clone().try_upvars_resolved(self) { + let scrutinee_place = scrutinee_builder.into_place(self); self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place); } @@ -348,12 +346,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ``` let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None; let scrutinee_place: Place<'tcx>; - if let Ok(scrutinee_builder) = scrutinee_place_builder - .clone() - .try_upvars_resolved(this.tcx, this.typeck_results) + if let Ok(scrutinee_builder) = + scrutinee_place_builder.clone().try_upvars_resolved(this) { - scrutinee_place = - scrutinee_builder.into_place(this.tcx, this.typeck_results); + scrutinee_place = scrutinee_builder.into_place(this); opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span)); } let scope = this.declare_bindings( @@ -602,12 +598,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { while let Some(next) = { for binding in &candidate_ref.bindings { let local = self.var_local_id(binding.var_id, OutsideGuard); - - let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( - VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, - )))) = self.local_decls[local].local_info else { - bug!("Let binding to non-user variable.") - }; // `try_upvars_resolved` may fail if it is unable to resolve the given // `PlaceBuilder` inside a closure. In this case, we don't want to include // a scrutinee place. `scrutinee_place_builder` will fail for destructured @@ -622,10 +612,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // let (v1, v2) = foo; // }; // ``` - if let Ok(match_pair_resolved) = - initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results) - { - let place = match_pair_resolved.into_place(self.tcx, self.typeck_results); + if let Ok(match_pair_resolved) = initializer.clone().try_upvars_resolved(self) { + let place = match_pair_resolved.into_place(self); + + let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, + )))) = self.local_decls[local].local_info else { + bug!("Let binding to non-user variable.") + }; + *match_place = Some(place); } } @@ -1605,9 +1600,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Insert a Shallow borrow of any places that is switched on. if let Some(fb) = fake_borrows && let Ok(match_place_resolved) = - match_place.clone().try_upvars_resolved(self.tcx, self.typeck_results) + match_place.clone().try_upvars_resolved(self) { - let resolved_place = match_place_resolved.into_place(self.tcx, self.typeck_results); + let resolved_place = match_place_resolved.into_place(self); fb.insert(resolved_place); } @@ -1799,10 +1794,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None; let expr_place: Place<'tcx>; - if let Ok(expr_builder) = - expr_place_builder.try_upvars_resolved(self.tcx, self.typeck_results) - { - expr_place = expr_builder.into_place(self.tcx, self.typeck_results); + if let Ok(expr_builder) = expr_place_builder.try_upvars_resolved(self) { + expr_place = expr_builder.into_place(self); opt_expr_place = Some((Some(&expr_place), expr_span)); } let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap(); diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index 878b17ce5a4..6fa817da28a 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -156,12 +156,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ascription: thir::Ascription { ref annotation, variance }, } => { // Apply the type ascription to the value at `match_pair.place`, which is the - if let Ok(place_resolved) = - match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results) - { + if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) { candidate.ascriptions.push(Ascription { annotation: annotation.clone(), - source: place_resolved.into_place(self.tcx, self.typeck_results), + source: place_resolved.into_place(self), variance, }); } @@ -185,12 +183,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ref subpattern, is_primary: _, } => { - if let Ok(place_resolved) = - match_pair.place.clone().try_upvars_resolved(self.tcx, self.typeck_results) - { + if let Ok(place_resolved) = match_pair.place.clone().try_upvars_resolved(self) { candidate.bindings.push(Binding { span: match_pair.pattern.span, - source: place_resolved.into_place(self.tcx, self.typeck_results), + source: place_resolved.into_place(self), var_id: var, binding_mode: mode, }); diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 1ef2524f136..92f00560f01 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -155,10 +155,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { make_target_blocks: impl FnOnce(&mut Self) -> Vec, ) { let place: Place<'tcx>; - if let Ok(test_place_builder) = - place_builder.try_upvars_resolved(self.tcx, self.typeck_results) - { - place = test_place_builder.into_place(self.tcx, self.typeck_results); + if let Ok(test_place_builder) = place_builder.try_upvars_resolved(self) { + place = test_place_builder.into_place(self); } else { return; } diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 9a1e98d3bb1..38839e63059 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -31,21 +31,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { suffix: &'pat [Pat<'tcx>], ) { let tcx = self.tcx; - let (min_length, exact_size) = if let Ok(place_resolved) = - place.clone().try_upvars_resolved(tcx, self.typeck_results) - { - match place_resolved - .into_place(tcx, self.typeck_results) - .ty(&self.local_decls, tcx) - .ty - .kind() - { - ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true), - _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), - } - } else { - ((prefix.len() + suffix.len()).try_into().unwrap(), false) - }; + let (min_length, exact_size) = + if let Ok(place_resolved) = place.clone().try_upvars_resolved(self) { + match place_resolved.into_place(self).ty(&self.local_decls, tcx).ty.kind() { + ty::Array(_, length) => (length.eval_usize(tcx, self.param_env), true), + _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), + } + } else { + ((prefix.len() + suffix.len()).try_into().unwrap(), false) + }; match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { let elem = @@ -100,7 +94,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { - pub(crate) fn new( + pub(in crate::build) fn new( place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, ) -> MatchPair<'pat, 'tcx> { From 7a8a048b5853c6e44af53d924a7c0feb2140e5af Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 24 Jun 2022 14:53:59 +0000 Subject: [PATCH 11/30] Remove early return that would likely have caused miscompilations if it ever happened --- .../rustc_mir_build/src/build/matches/test.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 92f00560f01..4e2a137c3fc 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -154,19 +154,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { test: &Test<'tcx>, make_target_blocks: impl FnOnce(&mut Self) -> Vec, ) { - let place: Place<'tcx>; - if let Ok(test_place_builder) = place_builder.try_upvars_resolved(self) { - place = test_place_builder.into_place(self); - } else { - return; - } - debug!( - "perform_test({:?}, {:?}: {:?}, {:?})", - block, - place, - place.ty(&self.local_decls, self.tcx), - test - ); + let place = place_builder.into_place(self); + let place_ty = place.ty(&self.local_decls, self.tcx); + debug!(?place, ?place_ty,); let source_info = self.source_info(test.span); match test.kind { From 0c6918fe210ebed9639c2f1b1fbfac4eb0fef38a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jun 2022 14:46:36 +0000 Subject: [PATCH 12/30] Reuse a helper method instead of manually rolling it --- compiler/rustc_mir_build/src/build/matches/test.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index 4e2a137c3fc..63acd731db7 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -724,9 +724,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`, // we want to create a set of derived match-patterns like // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`. - let elem = - ProjectionElem::Downcast(Some(adt_def.variant(variant_index).name), variant_index); - let downcast_place = match_pair.place.project(elem); // `(x as Variant)` + let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)` let consequent_match_pairs = subpatterns.iter().map(|subpattern| { // e.g., `(x as Variant).0` let place = downcast_place.clone().field(subpattern.field, subpattern.pattern.ty); From 12457f814cffc05ece8ad588d83aa87b69d78296 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jun 2022 16:16:11 +0000 Subject: [PATCH 13/30] Some tracing helpers --- compiler/rustc_mir_build/src/build/expr/as_place.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 98502737dc5..4bd7ef943b9 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -202,6 +202,7 @@ fn find_capture_matching_projections<'a, 'tcx>( /// `PlaceBuilder` now starts from `PlaceBase::Local`. /// /// Returns a Result with the error being the PlaceBuilder (`from_builder`) that was not found. +#[instrument(level = "trace", skip(cx))] fn to_upvars_resolved_place_builder<'tcx>( from_builder: PlaceBuilder<'tcx>, cx: &Builder<'_, 'tcx>, @@ -270,12 +271,14 @@ fn to_upvars_resolved_place_builder<'tcx>( // We used some of the projections to build the capture itself, // now we apply the remaining to the upvar resolved place. + trace!(?capture.place, ?from_builder.projection); let remaining_projections = strip_prefix( capture.place.base_ty, from_builder.projection, &capture.place.projections, ); upvar_resolved_place_builder.projection.extend(remaining_projections); + trace!(?upvar_resolved_place_builder); Ok(upvar_resolved_place_builder) } From 728c7e8bda215dc0ef055a817964f199427512e0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jun 2022 16:26:41 +0000 Subject: [PATCH 14/30] Allow destructuring opaque types, since the patterns constrain the opaque types --- .../src/build/expr/as_place.rs | 33 ++++++++++++++++--- .../rustc_mir_build/src/build/matches/mod.rs | 2 +- .../rustc_mir_build/src/build/matches/util.rs | 4 +++ .../src/thir/pattern/usefulness.rs | 6 +++- .../cross_inference_pattern_bug.rs | 3 +- .../cross_inference_pattern_bug.stderr | 10 ------ .../cross_inference_pattern_bug_no_type.rs | 6 ++-- ...cross_inference_pattern_bug_no_type.stderr | 10 ------ .../issue-96572-unconstrained-mismatch.rs | 10 ++++++ .../issue-96572-unconstrained-mismatch.stderr | 15 +++++++++ .../issue-96572-unconstrained-struct.rs | 11 +++++++ .../issue-96572-unconstrained-upvar-enum.rs | 13 ++++++++ .../issue-96572-unconstrained-upvar.rs | 13 ++++++++ .../issue-96572-unconstrained.rs | 11 +++++++ 14 files changed, 116 insertions(+), 31 deletions(-) delete mode 100644 src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr delete mode 100644 src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.stderr create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-struct.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar-enum.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained.rs diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 4bd7ef943b9..6abf419dd49 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -7,6 +7,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::hir::place::Projection as HirProjection; use rustc_middle::hir::place::ProjectionKind as HirProjectionKind; use rustc_middle::middle::region; +use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::AssertKind::BoundsCheck; use rustc_middle::mir::*; use rustc_middle::thir::*; @@ -104,8 +105,9 @@ fn convert_to_hir_projections_and_truncate_for_capture<'tcx>( variant = Some(*idx); continue; } + // These do not affect anything, they just make sure we know the right type. + ProjectionElem::OpaqueCast(_) => continue, ProjectionElem::Index(..) - | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => { // We don't capture array-access projections. @@ -297,16 +299,21 @@ fn strip_prefix<'tcx>( prefix_projections: &[HirProjection<'tcx>], ) -> impl Iterator> { let mut iter = projections.into_iter(); + let mut next = || match iter.next()? { + // Filter out opaque casts, they are unnecessary in the prefix. + ProjectionElem::OpaqueCast(..) => iter.next(), + other => Some(other), + }; for projection in prefix_projections { match projection.kind { HirProjectionKind::Deref => { - assert!(matches!(iter.next(), Some(ProjectionElem::Deref))); + assert!(matches!(next(), Some(ProjectionElem::Deref))); } HirProjectionKind::Field(..) => { if base_ty.is_enum() { - assert!(matches!(iter.next(), Some(ProjectionElem::Downcast(..)))); + assert!(matches!(next(), Some(ProjectionElem::Downcast(..)))); } - assert!(matches!(iter.next(), Some(ProjectionElem::Field(..)))); + assert!(matches!(next(), Some(ProjectionElem::Field(..)))); } HirProjectionKind::Index | HirProjectionKind::Subslice => { bug!("unexpected projection kind: {:?}", projection); @@ -320,7 +327,23 @@ fn strip_prefix<'tcx>( impl<'tcx> PlaceBuilder<'tcx> { pub(crate) fn into_place(self, cx: &Builder<'_, 'tcx>) -> Place<'tcx> { if let PlaceBase::Local(local) = self.base { - Place { local, projection: cx.tcx.intern_place_elems(&self.projection) } + let mut projections = vec![]; + let mut ty = PlaceTy::from_ty(cx.local_decls[local].ty); + for projection in self.projection { + // Only preserve those opaque casts that actually go from an opaque type + // to another type. + if let ProjectionElem::OpaqueCast(t) = projection { + if let ty::Opaque(..) = ty.ty.kind() { + if t != ty.ty { + projections.push(ProjectionElem::OpaqueCast(t)); + } + } + } else { + projections.push(projection); + } + ty = ty.projection_ty(cx.tcx, projection); + } + Place { local, projection: cx.tcx.intern_place_elems(&projections) } } else { self.expect_upvars_resolved(cx).into_place(cx) } diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 7fc28d624da..86ef666eac8 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -867,7 +867,7 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { Candidate { span: pattern.span, has_guard, - match_pairs: smallvec![MatchPair { place, pattern }], + match_pairs: smallvec![MatchPair::new(place, pattern)], bindings: Vec::new(), ascriptions: Vec::new(), subcandidates: Vec::new(), diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs index 38839e63059..4a7edc517f4 100644 --- a/compiler/rustc_mir_build/src/build/matches/util.rs +++ b/compiler/rustc_mir_build/src/build/matches/util.rs @@ -98,6 +98,10 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { place: PlaceBuilder<'tcx>, pattern: &'pat Pat<'tcx>, ) -> MatchPair<'pat, 'tcx> { + // Force the place type to the pattern's type. + // FIXME(oli-obk): only do this when we don't already know the place type. + // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? + let place = place.project(ProjectionElem::OpaqueCast(pattern.ty)); MatchPair { place, pattern } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 9e7a267ecbd..3203f801fa0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -967,7 +967,11 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( }) .collect(); - let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); + // In case we're matching on an opaque type in its defining scope, the patterns define the hidden type. + // The wildcard pattern needs to have the same type, otherwise it will always be deemed useful, even if the + // match is exhaustive for the pattern type. + let wild_ty = arms.first().map_or(scrut_ty, |arm| arm.pat.ty()); + let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(wild_ty)); let v = PatStack::from_pattern(wild_pattern); let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true); let non_exhaustiveness_witnesses = match usefulness { diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs index 811832848d9..9a50c0f988a 100644 --- a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs +++ b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.rs @@ -1,8 +1,9 @@ // compile-flags: --edition=2021 +// check-pass #![feature(type_alias_impl_trait)] fn main() { - type T = impl Copy; //~ ERROR unconstrained opaque type + type T = impl Copy; let foo: T = (1u32, 2u32); let (a, b): (u32, u32) = foo; } diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr deleted file mode 100644 index 03b172e6de5..00000000000 --- a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unconstrained opaque type - --> $DIR/cross_inference_pattern_bug.rs:5:14 - | -LL | type T = impl Copy; - | ^^^^^^^^^ - | - = note: `T` must be used in combination with a concrete type within the same module - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs index 328096d44b4..b929122a6c2 100644 --- a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs +++ b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs @@ -1,13 +1,13 @@ -// known-bug: #96572 // compile-flags: --edition=2021 --crate-type=lib // rustc-env:RUST_BACKTRACE=0 +// check-pass // tracked in https://github.com/rust-lang/rust/issues/96572 #![feature(type_alias_impl_trait)] fn main() { - type T = impl Copy; // error: unconstrained opaque type + type T = impl Copy; let foo: T = (1u32, 2u32); - let (a, b) = foo; // removing this line makes the code compile + let (a, b) = foo; // this line used to make the code fail } diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr deleted file mode 100644 index 8aa1f495639..00000000000 --- a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unconstrained opaque type - --> $DIR/cross_inference_pattern_bug_no_type.rs:10:14 - | -LL | type T = impl Copy; // error: unconstrained opaque type - | ^^^^^^^^^ - | - = note: `T` must be used in combination with a concrete type within the same module - -error: aborting due to previous error - diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.rs new file mode 100644 index 00000000000..825710851b0 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.rs @@ -0,0 +1,10 @@ +#![feature(type_alias_impl_trait)] + +fn main() { + type T = impl Copy; + let foo: T = Some((1u32, 2u32)); + match foo { + None => (), + Some((a, b, c)) => (), //~ ERROR mismatched types + } +} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.stderr b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.stderr new file mode 100644 index 00000000000..728244a1844 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-mismatch.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/issue-96572-unconstrained-mismatch.rs:8:14 + | +LL | match foo { + | --- this expression has type `T` +LL | None => (), +LL | Some((a, b, c)) => (), + | ^^^^^^^^^ expected a tuple with 2 elements, found one with 3 elements + | + = note: expected tuple `(u32, u32)` + found tuple `(_, _, _)` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-struct.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-struct.rs new file mode 100644 index 00000000000..3351d9bcff1 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-struct.rs @@ -0,0 +1,11 @@ +#![feature(type_alias_impl_trait)] +// check-pass + +#[derive(Copy, Clone)] +struct Foo((u32, u32)); + +fn main() { + type U = impl Copy; + let foo: U = Foo((1u32, 2u32)); + let Foo((a, b)) = foo; +} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar-enum.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar-enum.rs new file mode 100644 index 00000000000..ef3279a98d1 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar-enum.rs @@ -0,0 +1,13 @@ +#![feature(type_alias_impl_trait)] +// check-pass + +fn main() { + type T = impl Copy; + let foo: T = Some((1u32, 2u32)); + let x = move || { + match foo { + None => (), + Some((a, b)) => (), + } + }; +} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar.rs new file mode 100644 index 00000000000..bb0fc7c7534 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-upvar.rs @@ -0,0 +1,13 @@ +#![feature(type_alias_impl_trait)] +// check-pass + +#[derive(Copy, Clone)] +struct Foo((u32, u32)); + +fn main() { + type T = impl Copy; + let foo: T = Foo((1u32, 2u32)); + let x = move || { + let Foo((a, b)) = foo; + }; +} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained.rs new file mode 100644 index 00000000000..4b9ed7f28eb --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained.rs @@ -0,0 +1,11 @@ +#![feature(type_alias_impl_trait)] +// check-pass + +fn main() { + type T = impl Copy; + let foo: T = Some((1u32, 2u32)); + match foo { + None => (), + Some((a, b)) => (), + } +} From 6b33d5bfa94f0005c2ae7136f4cda14a396e42bc Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jun 2022 07:17:43 +0000 Subject: [PATCH 15/30] Make destructuring a defining use --- compiler/rustc_borrowck/src/type_check/mod.rs | 8 ++++++++ .../issue-96572-unconstrained-only-pattern.rs | 12 ++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index e139a73efc5..8e763a02af3 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -793,6 +793,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { ProjectionElem::OpaqueCast(ty) => { let ty = self.sanitize_type(place, ty); let ty = self.cx.normalize(ty, location); + self.cx + .eq_types( + base.ty, + ty, + location.to_locations(), + ConstraintCategory::TypeAnnotation, + ) + .unwrap(); PlaceTy::from_ty(ty) } } diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs new file mode 100644 index 00000000000..3f0445f5037 --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs @@ -0,0 +1,12 @@ +#![feature(type_alias_impl_trait)] +// check-pass + +type T = impl Copy; + +fn foo(foo: T) { + let (mut x, mut y) = foo; + x = 42; + y = "foo"; +} + +fn main() {} From ea68ce7fac0a5888e54e5725066b04fcd4290efa Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jun 2022 08:00:25 +0000 Subject: [PATCH 16/30] Revert a hack that only ever worked on level deep --- compiler/rustc_mir_build/src/thir/pattern/usefulness.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 3203f801fa0..9e7a267ecbd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -967,11 +967,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>( }) .collect(); - // In case we're matching on an opaque type in its defining scope, the patterns define the hidden type. - // The wildcard pattern needs to have the same type, otherwise it will always be deemed useful, even if the - // match is exhaustive for the pattern type. - let wild_ty = arms.first().map_or(scrut_ty, |arm| arm.pat.ty()); - let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(wild_ty)); + let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty)); let v = PatStack::from_pattern(wild_pattern); let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, scrut_hir_id, false, true); let non_exhaustiveness_witnesses = match usefulness { From 92e470a1de4f809af62d38df9a968c41f8887189 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jun 2022 10:49:55 +0000 Subject: [PATCH 17/30] Move constructor into the branch that actually uses it --- .../rustc_mir_build/src/thir/pattern/usefulness.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 9e7a267ecbd..a17d8f33630 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -806,11 +806,6 @@ fn is_useful<'p, 'tcx>( debug_assert!(rows.iter().all(|r| r.len() == v.len())); - let ty = v.head().ty(); - let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); - debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span()); - let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; - // If the first pattern is an or-pattern, expand it. let mut ret = Usefulness::new_not_useful(witness_preference); if v.head().is_or_pat() { @@ -832,6 +827,11 @@ fn is_useful<'p, 'tcx>( } } } else { + let ty = v.head().ty(); + let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); + debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span()); + let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; + let v_ctor = v.head().ctor(); debug!(?v_ctor); if let Constructor::IntRange(ctor_range) = &v_ctor { From 57e9f7a556ae4873acc14c62bb4d6fc7ed6123ac Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jun 2022 11:19:38 +0000 Subject: [PATCH 18/30] Infer wildcard type from other patterns at every pattern level --- .../src/thir/pattern/deconstruct_pat.rs | 37 +++++++++---------- .../src/thir/pattern/usefulness.rs | 20 +++++++--- ...e-96572-unconstrained-only-pattern-rpit.rs | 29 +++++++++++++++ .../issue-96572-unconstrained-only-pattern.rs | 12 ++++++ 4 files changed, 72 insertions(+), 26 deletions(-) create mode 100644 src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern-rpit.rs diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 60db98073a3..c0fd19cf71c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -1202,35 +1202,32 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { /// Creates a new list of wildcard fields for a given constructor. The result must have a /// length of `constructor.arity()`. - pub(super) fn wildcards( - cx: &MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - constructor: &Constructor<'tcx>, - ) -> Self { + #[instrument(level = "trace")] + pub(super) fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self { let ret = match constructor { - Single | Variant(_) => match ty.kind() { - ty::Tuple(fs) => Fields::wildcards_from_tys(cx, fs.iter()), - ty::Ref(_, rty, _) => Fields::wildcards_from_tys(cx, once(*rty)), + Single | Variant(_) => match pcx.ty.kind() { + ty::Tuple(fs) => Fields::wildcards_from_tys(pcx.cx, fs.iter()), + ty::Ref(_, rty, _) => Fields::wildcards_from_tys(pcx.cx, once(*rty)), ty::Adt(adt, substs) => { if adt.is_box() { // The only legal patterns of type `Box` (outside `std`) are `_` and box // patterns. If we're here we can assume this is a box pattern. - Fields::wildcards_from_tys(cx, once(substs.type_at(0))) + Fields::wildcards_from_tys(pcx.cx, once(substs.type_at(0))) } else { let variant = &adt.variant(constructor.variant_index_for_adt(*adt)); - let tys = Fields::list_variant_nonhidden_fields(cx, ty, variant) + let tys = Fields::list_variant_nonhidden_fields(pcx.cx, pcx.ty, variant) .map(|(_, ty)| ty); - Fields::wildcards_from_tys(cx, tys) + Fields::wildcards_from_tys(pcx.cx, tys) } } - _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), + _ => bug!("Unexpected type for `Single` constructor: {:?}", pcx), }, - Slice(slice) => match *ty.kind() { + Slice(slice) => match *pcx.ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { let arity = slice.arity(); - Fields::wildcards_from_tys(cx, (0..arity).map(|_| ty)) + Fields::wildcards_from_tys(pcx.cx, (0..arity).map(|_| ty)) } - _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), + _ => bug!("bad slice pattern {:?} {:?}", constructor, pcx), }, Str(..) | FloatRange(..) @@ -1243,7 +1240,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { bug!("called `Fields::wildcards` on an `Or` ctor") } }; - debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); + debug!(?ret); ret } @@ -1286,7 +1283,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern /// `Some(_)`. pub(super) fn wild_from_ctor(pcx: PatCtxt<'_, 'p, 'tcx>, ctor: Constructor<'tcx>) -> Self { - let fields = Fields::wildcards(pcx.cx, pcx.ty, &ctor); + let fields = Fields::wildcards(pcx, &ctor); DeconstructedPat::new(ctor, fields, pcx.ty, DUMMY_SP) } @@ -1553,13 +1550,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { /// `other_ctor` can be different from `self.ctor`, but must be covered by it. pub(super) fn specialize<'a>( &'a self, - cx: &MatchCheckCtxt<'p, 'tcx>, + pcx: PatCtxt<'_, 'p, 'tcx>, other_ctor: &Constructor<'tcx>, ) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> { match (&self.ctor, other_ctor) { (Wildcard, _) => { // We return a wildcard for each field of `other_ctor`. - Fields::wildcards(cx, self.ty, other_ctor).iter_patterns().collect() + Fields::wildcards(pcx, other_ctor).iter_patterns().collect() } (Slice(self_slice), Slice(other_slice)) if self_slice.arity() != other_slice.arity() => @@ -1578,7 +1575,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> { let prefix = &self.fields.fields[..prefix]; let suffix = &self.fields.fields[self_slice.arity() - suffix..]; let wildcard: &_ = - cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty)); + pcx.cx.pattern_arena.alloc(DeconstructedPat::wildcard(inner_ty)); let extra_wildcards = other_slice.arity() - self_slice.arity(); let extra_wildcards = (0..extra_wildcards).map(|_| wildcard); prefix.iter().chain(extra_wildcards).chain(suffix).collect() diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index a17d8f33630..f27ec22a8de 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -411,12 +411,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { /// This is roughly the inverse of `Constructor::apply`. fn pop_head_constructor( &self, - cx: &MatchCheckCtxt<'p, 'tcx>, + pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>, ) -> PatStack<'p, 'tcx> { // We pop the head pattern and push the new fields extracted from the arguments of // `self.head()`. - let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(cx, ctor); + let mut new_fields: SmallVec<[_; 2]> = self.head().specialize(pcx, ctor); new_fields.extend_from_slice(&self.pats[1..]); PatStack::from_vec(new_fields) } @@ -475,7 +475,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { let mut matrix = Matrix::empty(); for row in &self.patterns { if ctor.is_covered_by(pcx, row.head().ctor()) { - let new_row = row.pop_head_constructor(pcx.cx, ctor); + let new_row = row.pop_head_constructor(pcx, ctor); matrix.push(new_row); } } @@ -786,7 +786,7 @@ fn is_useful<'p, 'tcx>( is_under_guard: bool, is_top_level: bool, ) -> Usefulness<'p, 'tcx> { - debug!("matrix,v={:?}{:?}", matrix, v); + debug!(?matrix, ?v); let Matrix { patterns: rows, .. } = matrix; // The base case. We are pattern-matching on () and the return value is @@ -827,7 +827,15 @@ fn is_useful<'p, 'tcx>( } } } else { - let ty = v.head().ty(); + let mut ty = v.head().ty(); + + // Opaque types can't get destructured/split, but the patterns can + // actually hint at hidden types, so we use the patterns' types instead. + if let ty::Opaque(..) = v.head().ty().kind() { + if let Some(row) = rows.first() { + ty = row.head().ty(); + } + } let is_non_exhaustive = cx.is_foreign_non_exhaustive_enum(ty); debug!("v.head: {:?}, v.span: {:?}", v.head(), v.head().span()); let pcx = PatCtxt { cx, ty, span: v.head().span(), is_top_level, is_non_exhaustive }; @@ -853,7 +861,7 @@ fn is_useful<'p, 'tcx>( debug!("specialize({:?})", ctor); // We cache the result of `Fields::wildcards` because it is used a lot. let spec_matrix = start_matrix.specialize_constructor(pcx, &ctor); - let v = v.pop_head_constructor(cx, &ctor); + let v = v.pop_head_constructor(pcx, &ctor); let usefulness = ensure_sufficient_stack(|| { is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false) }); diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern-rpit.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern-rpit.rs new file mode 100644 index 00000000000..c0a371eca1c --- /dev/null +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern-rpit.rs @@ -0,0 +1,29 @@ +// check-pass + +#[allow(unconditional_recursion)] +fn foo(b: bool) -> impl Copy { + let (mut x, mut y) = foo(false); + x = 42; + y = "foo"; + if b { + panic!() + } else { + foo(true) + } +} + +fn bar(b: bool) -> Option { + if b { + return None; + } + match bar(!b) { + Some((mut x, mut y)) => { + x = 42; + y = "foo"; + } + None => {} + } + None +} + +fn main() {} diff --git a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs index 3f0445f5037..ec249958590 100644 --- a/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs +++ b/src/test/ui/type-alias-impl-trait/issue-96572-unconstrained-only-pattern.rs @@ -9,4 +9,16 @@ fn foo(foo: T) { y = "foo"; } +type U = impl Copy; + +fn bar(bar: Option) { + match bar { + Some((mut x, mut y)) => { + x = 42; + y = "foo"; + } + None => {} + } +} + fn main() {} From 1c8f87e9071a0eca3cb6ccab1ca2cc3d5ea2ad73 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 15 Jul 2022 16:30:07 +0000 Subject: [PATCH 19/30] Revert "Highlight conflicting param-env candidates" This reverts commit 08135254dcf22be0d5661ea8f75e703b29a83514. --- compiler/rustc_middle/src/traits/mod.rs | 10 +---- .../src/traits/error_reporting/mod.rs | 41 +++++------------- .../src/traits/select/candidate_assembly.rs | 43 +------------------ src/test/ui/issues/issue-21974.stderr | 8 +--- src/test/ui/issues/issue-24424.stderr | 6 +-- src/test/ui/lifetimes/issue-34979.stderr | 8 +--- src/test/ui/traits/issue-85735.stderr | 9 +--- .../ui/type/type-check/issue-40294.stderr | 8 +--- 8 files changed, 19 insertions(+), 114 deletions(-) diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index d8cc7d3feb0..3e4e56888ff 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -541,17 +541,9 @@ pub enum SelectionError<'tcx> { ErrorReporting, /// Multiple applicable `impl`s where found. The `DefId`s correspond to /// all the `impl`s' Items. - Ambiguous(Vec), + Ambiguous(Vec), } -#[derive(Copy, Clone, Debug)] -pub enum AmbiguousSelection { - Impl(DefId), - ParamEnv(Span), -} - -TrivialTypeTraversalAndLiftImpls! { AmbiguousSelection, } - /// When performing resolution, it is typically the case that there /// can be one of three outcomes: /// diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 39fce3cf769..3960f391407 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -23,7 +23,7 @@ use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; use rustc_infer::infer::error_reporting::same_type_modulo_infer; -use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; +use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; @@ -1402,7 +1402,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[AmbiguousSelection], + impls: &[DefId], predicate: ty::Predicate<'tcx>, ); @@ -2035,14 +2035,6 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { ); match selcx.select_from_obligation(&obligation) { Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => { - if self.is_tainted_by_errors() && subst.is_none() { - // If `subst.is_none()`, then this is probably two param-env - // candidates or impl candidates that are equal modulo lifetimes. - // Therefore, if we've already emitted an error, just skip this - // one, since it's not particularly actionable. - err.cancel(); - return; - } self.annotate_source_of_ambiguity(&mut err, &impls, predicate); } _ => { @@ -2223,35 +2215,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[AmbiguousSelection], + impls: &[DefId], predicate: ty::Predicate<'tcx>, ) { let mut spans = vec![]; let mut crates = vec![]; let mut post = vec![]; - let mut or_where_clause = false; - for ambig in impls { - match ambig { - AmbiguousSelection::Impl(def_id) => match self.tcx.span_of_impl(*def_id) { - Ok(span) => spans.push(span), - Err(name) => { - crates.push(name); - if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { - post.push(header); - } + for def_id in impls { + match self.tcx.span_of_impl(*def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { + post.push(header); } - }, - AmbiguousSelection::ParamEnv(span) => { - or_where_clause = true; - spans.push(*span); } } } - let msg = format!( - "multiple `impl`s{} satisfying `{}` found", - if or_where_clause { " or `where` clauses" } else { "" }, - predicate - ); + let msg = format!("multiple `impl`s satisfying `{}` found", predicate); let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); crate_names.sort(); crate_names.dedup(); diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 96d83deeeb7..6e8581128dd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -6,11 +6,9 @@ //! //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly use hir::LangItem; -use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::traits::util::elaborate_predicates_with_span; -use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; +use rustc_infer::traits::TraitEngine; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -201,48 +199,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // and report ambiguity. if i > 1 { debug!("multiple matches, ambig"); - - // Collect a list of (probable) spans that point to a param-env candidate - let tcx = self.infcx.tcx; - let owner = stack.obligation.cause.body_id.owner.to_def_id(); - let predicates = tcx.predicates_of(owner).instantiate_identity(tcx); - let param_env_spans: FxHashMap<_, _> = elaborate_predicates_with_span( - tcx, - std::iter::zip(predicates.predicates, predicates.spans), - ) - .filter_map(|obligation| { - let kind = obligation.predicate.kind(); - if let ty::PredicateKind::Trait(trait_pred) = kind.skip_binder() { - if trait_pred.trait_ref - == ty::TraitRef::identity(tcx, trait_pred.def_id()) - .skip_binder() - { - // HACK: Remap the `Self: Trait` predicate that every trait has to a more useful span - Some(( - kind.rebind(trait_pred), - tcx.def_span(trait_pred.def_id()), - )) - } else { - Some((kind.rebind(trait_pred), obligation.cause.span)) - } - } else { - None - } - }) - .collect(); - return Err(Ambiguous( candidates .into_iter() .filter_map(|c| match c.candidate { - SelectionCandidate::ImplCandidate(def_id) => { - Some(AmbiguousSelection::Impl(def_id)) - } - SelectionCandidate::ParamCandidate(predicate) => { - Some(AmbiguousSelection::ParamEnv( - *param_env_spans.get(&predicate)?, - )) - } + SelectionCandidate::ImplCandidate(def_id) => Some(def_id), _ => None, }) .collect(), diff --git a/src/test/ui/issues/issue-21974.stderr b/src/test/ui/issues/issue-21974.stderr index 2d60b18b1f2..4e010a13653 100644 --- a/src/test/ui/issues/issue-21974.stderr +++ b/src/test/ui/issues/issue-21974.stderr @@ -4,13 +4,7 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo` LL | where &'a T : Foo, | ^^^ | -note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found - --> $DIR/issue-21974.rs:11:19 - | -LL | where &'a T : Foo, - | ^^^ -LL | &'b T : Foo - | ^^^ + = note: cannot satisfy `&'a T: Foo` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-24424.stderr b/src/test/ui/issues/issue-24424.stderr index 50d7f988e19..8f3b2ac7319 100644 --- a/src/test/ui/issues/issue-24424.stderr +++ b/src/test/ui/issues/issue-24424.stderr @@ -4,11 +4,7 @@ error[E0283]: type annotations needed: cannot satisfy `T0: Trait0<'l0>` LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {} | ^^^^^^^^^^^ | -note: multiple `impl`s or `where` clauses satisfying `T0: Trait0<'l0>` found - --> $DIR/issue-24424.rs:4:57 - | -LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {} - | ^^^^^^^^^^^ ^^^^^^^^^^^ + = note: cannot satisfy `T0: Trait0<'l0>` error: aborting due to previous error diff --git a/src/test/ui/lifetimes/issue-34979.stderr b/src/test/ui/lifetimes/issue-34979.stderr index a78b3eaf625..5832c4d173c 100644 --- a/src/test/ui/lifetimes/issue-34979.stderr +++ b/src/test/ui/lifetimes/issue-34979.stderr @@ -4,13 +4,7 @@ error[E0283]: type annotations needed: cannot satisfy `&'a (): Foo` LL | &'a (): Foo, | ^^^ | -note: multiple `impl`s or `where` clauses satisfying `&'a (): Foo` found - --> $DIR/issue-34979.rs:6:13 - | -LL | &'a (): Foo, - | ^^^ -LL | &'static (): Foo; - | ^^^ + = note: cannot satisfy `&'a (): Foo` error: aborting due to previous error diff --git a/src/test/ui/traits/issue-85735.stderr b/src/test/ui/traits/issue-85735.stderr index 9e80497ca6e..fa280135beb 100644 --- a/src/test/ui/traits/issue-85735.stderr +++ b/src/test/ui/traits/issue-85735.stderr @@ -4,14 +4,7 @@ error[E0283]: type annotations needed: cannot satisfy `T: FnMut<(&'a (),)>` LL | T: FnMut(&'a ()), | ^^^^^^^^^^^^^ | -note: multiple `impl`s or `where` clauses satisfying `T: FnMut<(&'a (),)>` found - --> $DIR/issue-85735.rs:7:8 - | -LL | T: FnMut(&'a ()), - | ^^^^^^^^^^^^^ -LL | -LL | T: FnMut(&'b ()), - | ^^^^^^^^^^^^^ + = note: cannot satisfy `T: FnMut<(&'a (),)>` error: aborting due to previous error diff --git a/src/test/ui/type/type-check/issue-40294.stderr b/src/test/ui/type/type-check/issue-40294.stderr index d15fd23418b..75feb5698eb 100644 --- a/src/test/ui/type/type-check/issue-40294.stderr +++ b/src/test/ui/type/type-check/issue-40294.stderr @@ -4,13 +4,7 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo` LL | where &'a T : Foo, | ^^^ | -note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found - --> $DIR/issue-40294.rs:6:19 - | -LL | where &'a T : Foo, - | ^^^ -LL | &'b T : Foo - | ^^^ + = note: cannot satisfy `&'a T: Foo` error: aborting due to previous error From 20b5aaf11103d65752adafe549be1acecfa9acdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 14 Jul 2022 17:12:01 -0700 Subject: [PATCH 20/30] Provide structured suggestion for dropped temp value --- .../src/diagnostics/conflict_errors.rs | 33 ++++++++++++++- .../borrowck-borrowed-uniq-rvalue.fixed | 14 +++++++ .../borrowck/borrowck-borrowed-uniq-rvalue.rs | 6 +-- .../borrowck-borrowed-uniq-rvalue.stderr | 8 +++- src/test/ui/cleanup-rvalue-scopes-cf.stderr | 42 +++++++++++++++---- src/test/ui/issues/issue-11493.fixed | 9 ++++ src/test/ui/issues/issue-11493.rs | 3 +- src/test/ui/issues/issue-11493.stderr | 12 ++++-- src/test/ui/issues/issue-36082.fixed | 17 ++++++++ src/test/ui/issues/issue-36082.rs | 3 +- src/test/ui/issues/issue-36082.stderr | 8 +++- .../ui/nll/borrowed-temporary-error.stderr | 8 +++- .../span/borrowck-let-suggestion-suffixes.rs | 7 ++-- .../borrowck-let-suggestion-suffixes.stderr | 24 +++++++++-- .../ui/span/borrowck-ref-into-rvalue.fixed | 13 ++++++ src/test/ui/span/borrowck-ref-into-rvalue.rs | 1 + .../ui/span/borrowck-ref-into-rvalue.stderr | 8 +++- src/test/ui/span/issue-15480.fixed | 14 +++++++ src/test/ui/span/issue-15480.rs | 1 + src/test/ui/span/issue-15480.stderr | 9 +++- 20 files changed, 205 insertions(+), 35 deletions(-) create mode 100644 src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.fixed create mode 100644 src/test/ui/issues/issue-11493.fixed create mode 100644 src/test/ui/issues/issue-36082.fixed create mode 100644 src/test/ui/span/borrowck-ref-into-rvalue.fixed create mode 100644 src/test/ui/span/issue-15480.fixed diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 0ceb63477c8..6923fc1c1f6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1500,7 +1500,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | BorrowExplanation::UsedLaterInLoop(..) | BorrowExplanation::UsedLaterWhenDropped { .. } => { // Only give this note and suggestion if it could be relevant. - err.note("consider using a `let` binding to create a longer lived value"); + let sm = self.infcx.tcx.sess.source_map(); + let mut suggested = false; + let msg = "consider using a `let` binding to create a longer lived value"; + if let Some(scope) = + self.body.source_scopes.get(self.body.source_info(location).scope) + && let ClearCrossCrate::Set(scope_data) = &scope.local_data + && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root) + && let Some(id) = node.body_id() + && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind + { + for stmt in block.stmts { + if stmt.span.contains(proper_span) + && let Some(p) = sm.span_to_margin(stmt.span) + && let Ok(s) = sm.span_to_snippet(proper_span) + { + let addition = format!("let binding = {};\n{}", s, " ".repeat(p)); + err.multipart_suggestion_verbose( + msg, + vec![ + (stmt.span.shrink_to_lo(), addition), + (proper_span, "binding".to_string()), + ], + Applicability::MaybeIncorrect, + ); + suggested = true; + break; + } + } + } + if !suggested { + err.note(msg); + } } _ => {} } diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.fixed b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.fixed new file mode 100644 index 00000000000..8bf6a2f6db3 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +use std::collections::HashMap; + +fn main() { + let tmp: Box<_>; + let mut buggy_map: HashMap = HashMap::new(); + let binding = Box::new(1); + buggy_map.insert(42, &*binding); //~ ERROR temporary value dropped while borrowed + + // but it is ok if we use a temporary + tmp = Box::new(2); + buggy_map.insert(43, &*tmp); +} diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.rs b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.rs index 1d05845fc6b..85481336a30 100644 --- a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.rs +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.rs @@ -1,9 +1,7 @@ +// run-rustfix + use std::collections::HashMap; - - - - fn main() { let tmp: Box<_>; let mut buggy_map: HashMap = HashMap::new(); diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr index 01379ed8512..dea8ac90bec 100644 --- a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/borrowck-borrowed-uniq-rvalue.rs:10:28 + --> $DIR/borrowck-borrowed-uniq-rvalue.rs:8:28 | LL | buggy_map.insert(42, &*Box::new(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement @@ -9,7 +9,11 @@ LL | buggy_map.insert(42, &*Box::new(1)); LL | buggy_map.insert(43, &*tmp); | --------------------------- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = Box::new(1); +LL ~ buggy_map.insert(42, &*binding); + | error: aborting due to previous error diff --git a/src/test/ui/cleanup-rvalue-scopes-cf.stderr b/src/test/ui/cleanup-rvalue-scopes-cf.stderr index 04e599755fb..40f14c38984 100644 --- a/src/test/ui/cleanup-rvalue-scopes-cf.stderr +++ b/src/test/ui/cleanup-rvalue-scopes-cf.stderr @@ -9,7 +9,11 @@ LL | let x1 = arg(&AddFlags(1)); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let x1 = arg(&binding); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:27:14 @@ -22,7 +26,11 @@ LL | let x2 = AddFlags(1).get(); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let x2 = binding.get(); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:28:21 @@ -35,7 +43,11 @@ LL | let x3 = &*arg(&AddFlags(1)); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let x3 = &*arg(&binding); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:29:24 @@ -48,7 +60,11 @@ LL | let ref x4 = *arg(&AddFlags(1)); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let ref x4 = *arg(&binding); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:30:24 @@ -61,7 +77,11 @@ LL | let &ref x5 = arg(&AddFlags(1)); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let &ref x5 = arg(&binding); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:31:14 @@ -74,7 +94,11 @@ LL | let x6 = AddFlags(1).get(); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let x6 = binding.get(); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:32:44 @@ -87,7 +111,11 @@ LL | LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let StackBox { f: x7 } = StackBox { f: binding.get() }; + | error: aborting due to 7 previous errors diff --git a/src/test/ui/issues/issue-11493.fixed b/src/test/ui/issues/issue-11493.fixed new file mode 100644 index 00000000000..139bd9a0739 --- /dev/null +++ b/src/test/ui/issues/issue-11493.fixed @@ -0,0 +1,9 @@ +// run-rustfix +fn id(x: T) -> T { x } + +fn main() { + let x = Some(3); + let binding = id(5); + let y = x.as_ref().unwrap_or(&binding); //~ ERROR + let _ = &y; +} diff --git a/src/test/ui/issues/issue-11493.rs b/src/test/ui/issues/issue-11493.rs index b28c173b19b..cb77f89fb2b 100644 --- a/src/test/ui/issues/issue-11493.rs +++ b/src/test/ui/issues/issue-11493.rs @@ -1,7 +1,8 @@ +// run-rustfix fn id(x: T) -> T { x } fn main() { let x = Some(3); let y = x.as_ref().unwrap_or(&id(5)); //~ ERROR - &y; + let _ = &y; } diff --git a/src/test/ui/issues/issue-11493.stderr b/src/test/ui/issues/issue-11493.stderr index f954d64ac5b..a5d1f2816f1 100644 --- a/src/test/ui/issues/issue-11493.stderr +++ b/src/test/ui/issues/issue-11493.stderr @@ -1,14 +1,18 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/issue-11493.rs:5:35 + --> $DIR/issue-11493.rs:6:35 | LL | let y = x.as_ref().unwrap_or(&id(5)); | ^^^^^ - temporary value is freed at the end of this statement | | | creates a temporary which is freed while still in use -LL | &y; - | -- borrow later used here +LL | let _ = &y; + | -- borrow later used here + | +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = id(5); +LL ~ let y = x.as_ref().unwrap_or(&binding); | - = note: consider using a `let` binding to create a longer lived value error: aborting due to previous error diff --git a/src/test/ui/issues/issue-36082.fixed b/src/test/ui/issues/issue-36082.fixed new file mode 100644 index 00000000000..8640ca7a509 --- /dev/null +++ b/src/test/ui/issues/issue-36082.fixed @@ -0,0 +1,17 @@ +// run-rustfix +use std::cell::RefCell; + +fn main() { + let mut r = 0; + let s = 0; + let x = RefCell::new((&mut r,s)); + + let binding = x.borrow(); + let val: &_ = binding.0; + //~^ ERROR temporary value dropped while borrowed [E0716] + //~| NOTE temporary value is freed at the end of this statement + //~| NOTE creates a temporary which is freed while still in use + //~| HELP consider using a `let` binding to create a longer lived value + println!("{}", val); + //~^ borrow later used here +} diff --git a/src/test/ui/issues/issue-36082.rs b/src/test/ui/issues/issue-36082.rs index a2ff477eb81..877d372fb84 100644 --- a/src/test/ui/issues/issue-36082.rs +++ b/src/test/ui/issues/issue-36082.rs @@ -1,3 +1,4 @@ +// run-rustfix use std::cell::RefCell; fn main() { @@ -9,7 +10,7 @@ fn main() { //~^ ERROR temporary value dropped while borrowed [E0716] //~| NOTE temporary value is freed at the end of this statement //~| NOTE creates a temporary which is freed while still in use - //~| NOTE consider using a `let` binding to create a longer lived value + //~| HELP consider using a `let` binding to create a longer lived value println!("{}", val); //~^ borrow later used here } diff --git a/src/test/ui/issues/issue-36082.stderr b/src/test/ui/issues/issue-36082.stderr index 26bf4cb1be8..4bd586db1cd 100644 --- a/src/test/ui/issues/issue-36082.stderr +++ b/src/test/ui/issues/issue-36082.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/issue-36082.rs:8:19 + --> $DIR/issue-36082.rs:9:19 | LL | let val: &_ = x.borrow().0; | ^^^^^^^^^^ - temporary value is freed at the end of this statement @@ -9,7 +9,11 @@ LL | let val: &_ = x.borrow().0; LL | println!("{}", val); | --- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = x.borrow(); +LL ~ let val: &_ = binding.0; + | error: aborting due to previous error diff --git a/src/test/ui/nll/borrowed-temporary-error.stderr b/src/test/ui/nll/borrowed-temporary-error.stderr index 2c6bd92641f..59e6dd61b0f 100644 --- a/src/test/ui/nll/borrowed-temporary-error.stderr +++ b/src/test/ui/nll/borrowed-temporary-error.stderr @@ -9,7 +9,13 @@ LL | }); LL | println!("{:?}", x); | - borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = (v,); +LL ~ let x = gimme({ +LL | let v = 22; +LL ~ &binding + | error: aborting due to previous error diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs index 4744f3710ce..70e136e7e61 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs @@ -20,9 +20,9 @@ fn f() { //~^ ERROR temporary value dropped while borrowed //~| NOTE creates a temporary which is freed while still in use //~| NOTE temporary value is freed at the end of this statement - //~| NOTE consider using a `let` binding to create a longer lived value + //~| HELP consider using a `let` binding to create a longer lived value - { + { //~ HELP consider using a `let` binding to create a longer lived value let mut v4 = Vec::new(); // (sub) statement 0 @@ -30,7 +30,6 @@ fn f() { //~^ ERROR temporary value dropped while borrowed //~| NOTE creates a temporary which is freed while still in use //~| NOTE temporary value is freed at the end of this statement - //~| NOTE consider using a `let` binding to create a longer lived value v4.use_ref(); //~^ NOTE borrow later used here } // (statement 7) @@ -41,7 +40,7 @@ fn f() { //~^ ERROR temporary value dropped while borrowed //~| NOTE creates a temporary which is freed while still in use //~| NOTE temporary value is freed at the end of this statement - //~| NOTE consider using a `let` binding to create a longer lived value + //~| HELP consider using a `let` binding to create a longer lived value v1.push(&old[0]); diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr index 7c5caba6eae..ab68d111597 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr @@ -21,7 +21,11 @@ LL | v3.push(&id('x')); // statement 6 LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref(); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = id('x'); +LL ~ v3.push(&binding); // statement 6 + | error[E0716]: temporary value dropped while borrowed --> $DIR/borrowck-let-suggestion-suffixes.rs:29:18 @@ -34,10 +38,18 @@ LL | v4.push(&id('y')); LL | v4.use_ref(); | ------------ borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = id('y'); +LL ~ { +LL | +LL | let mut v4 = Vec::new(); // (sub) statement 0 +LL | +LL ~ v4.push(&binding); + | error[E0716]: temporary value dropped while borrowed - --> $DIR/borrowck-let-suggestion-suffixes.rs:40:14 + --> $DIR/borrowck-let-suggestion-suffixes.rs:39:14 | LL | v5.push(&id('z')); | ^^^^^^^ - temporary value is freed at the end of this statement @@ -47,7 +59,11 @@ LL | v5.push(&id('z')); LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref(); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = id('z'); +LL ~ v5.push(&binding); + | error: aborting due to 4 previous errors diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.fixed b/src/test/ui/span/borrowck-ref-into-rvalue.fixed new file mode 100644 index 00000000000..51f65e5345d --- /dev/null +++ b/src/test/ui/span/borrowck-ref-into-rvalue.fixed @@ -0,0 +1,13 @@ +// run-rustfix +fn main() { + let msg; + let binding = Some("Hello".to_string()); + match binding { + //~^ ERROR temporary value dropped while borrowed + Some(ref m) => { + msg = m; + }, + None => { panic!() } + } + println!("{}", *msg); +} diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.rs b/src/test/ui/span/borrowck-ref-into-rvalue.rs index c11aa1af540..7b09fad927f 100644 --- a/src/test/ui/span/borrowck-ref-into-rvalue.rs +++ b/src/test/ui/span/borrowck-ref-into-rvalue.rs @@ -1,3 +1,4 @@ +// run-rustfix fn main() { let msg; match Some("Hello".to_string()) { diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.stderr b/src/test/ui/span/borrowck-ref-into-rvalue.stderr index 4f529ce9511..cb5289d24b4 100644 --- a/src/test/ui/span/borrowck-ref-into-rvalue.stderr +++ b/src/test/ui/span/borrowck-ref-into-rvalue.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/borrowck-ref-into-rvalue.rs:3:11 + --> $DIR/borrowck-ref-into-rvalue.rs:4:11 | LL | match Some("Hello".to_string()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use @@ -9,7 +9,11 @@ LL | } LL | println!("{}", *msg); | ---- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = Some("Hello".to_string()); +LL ~ match binding { + | error: aborting due to previous error diff --git a/src/test/ui/span/issue-15480.fixed b/src/test/ui/span/issue-15480.fixed new file mode 100644 index 00000000000..e6d1a4dd328 --- /dev/null +++ b/src/test/ui/span/issue-15480.fixed @@ -0,0 +1,14 @@ +// run-rustfix +fn id(x: T) -> T { x } + +fn main() { + let binding = id(3); + let v = vec![ + &binding + ]; + //~^^ ERROR temporary value dropped while borrowed + + for &&x in &v { + println!("{}", x + 3); + } +} diff --git a/src/test/ui/span/issue-15480.rs b/src/test/ui/span/issue-15480.rs index b286d94178a..916ce4b1edb 100644 --- a/src/test/ui/span/issue-15480.rs +++ b/src/test/ui/span/issue-15480.rs @@ -1,3 +1,4 @@ +// run-rustfix fn id(x: T) -> T { x } fn main() { diff --git a/src/test/ui/span/issue-15480.stderr b/src/test/ui/span/issue-15480.stderr index 23ee2256dd8..460ad9ac744 100644 --- a/src/test/ui/span/issue-15480.stderr +++ b/src/test/ui/span/issue-15480.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/issue-15480.rs:5:10 + --> $DIR/issue-15480.rs:6:10 | LL | &id(3) | ^^^^^ creates a temporary which is freed while still in use @@ -9,7 +9,12 @@ LL | ]; LL | for &&x in &v { | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = id(3); +LL ~ let v = vec![ +LL ~ &binding + | error: aborting due to previous error From 635c38187bbd7adc10abf417c68b1ff06753ff5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 14 Jul 2022 18:41:12 -0700 Subject: [PATCH 21/30] Avoid incorrect suggestion We check that there's a single level of block nesting to ensure always correct suggestions. If we don't, then we only provide a free-form message to avoid misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`. We could expand the analysis to suggest hoising all of the relevant parts of the users' code to make the code compile, but that could be too much. --- .../src/diagnostics/conflict_errors.rs | 40 +++++++++++++++++-- .../ui/nll/borrowed-temporary-error.stderr | 8 +--- .../span/borrowck-let-suggestion-suffixes.rs | 3 +- .../borrowck-let-suggestion-suffixes.stderr | 12 +----- 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 6923fc1c1f6..c4eed784b88 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1503,15 +1503,49 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let sm = self.infcx.tcx.sess.source_map(); let mut suggested = false; let msg = "consider using a `let` binding to create a longer lived value"; - if let Some(scope) = - self.body.source_scopes.get(self.body.source_info(location).scope) + + use rustc_hir::intravisit::Visitor; + + /// We check that there's a single level of block nesting to ensure always correct + /// suggestions. If we don't, then we only provide a free-form message to avoid + /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`. + /// We could expand the analysis to suggest hoising all of the relevant parts of + /// the users' code to make the code compile, but that could be too much. + struct NestedStatementVisitor { + span: Span, + current: usize, + found: usize, + } + + impl<'tcx> Visitor<'tcx> for NestedStatementVisitor { + fn visit_block(&mut self, block: &hir::Block<'tcx>) { + self.current += 1; + rustc_hir::intravisit::walk_block(self, block); + self.current -= 1; + } + fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) { + if self.span == expr.span { + self.found = self.current; + } + rustc_hir::intravisit::walk_expr(self, expr); + } + } + let source_info = self.body.source_info(location); + if let Some(scope) = self.body.source_scopes.get(source_info.scope) && let ClearCrossCrate::Set(scope_data) = &scope.local_data && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root) && let Some(id) = node.body_id() && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind { for stmt in block.stmts { - if stmt.span.contains(proper_span) + let mut visitor = NestedStatementVisitor { + span: proper_span, + current: 0, + found: 0, + }; + visitor.visit_stmt(stmt); + if visitor.found == 0 + && stmt.span.contains(proper_span) && let Some(p) = sm.span_to_margin(stmt.span) && let Ok(s) = sm.span_to_snippet(proper_span) { diff --git a/src/test/ui/nll/borrowed-temporary-error.stderr b/src/test/ui/nll/borrowed-temporary-error.stderr index 59e6dd61b0f..2c6bd92641f 100644 --- a/src/test/ui/nll/borrowed-temporary-error.stderr +++ b/src/test/ui/nll/borrowed-temporary-error.stderr @@ -9,13 +9,7 @@ LL | }); LL | println!("{:?}", x); | - borrow later used here | -help: consider using a `let` binding to create a longer lived value - | -LL ~ let binding = (v,); -LL ~ let x = gimme({ -LL | let v = 22; -LL ~ &binding - | + = note: consider using a `let` binding to create a longer lived value error: aborting due to previous error diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs index 70e136e7e61..6240f103c99 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs @@ -22,7 +22,7 @@ fn f() { //~| NOTE temporary value is freed at the end of this statement //~| HELP consider using a `let` binding to create a longer lived value - { //~ HELP consider using a `let` binding to create a longer lived value + { let mut v4 = Vec::new(); // (sub) statement 0 @@ -30,6 +30,7 @@ fn f() { //~^ ERROR temporary value dropped while borrowed //~| NOTE creates a temporary which is freed while still in use //~| NOTE temporary value is freed at the end of this statement + //~| NOTE consider using a `let` binding to create a longer lived value v4.use_ref(); //~^ NOTE borrow later used here } // (statement 7) diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr index ab68d111597..a236dab3ae5 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr @@ -38,18 +38,10 @@ LL | v4.push(&id('y')); LL | v4.use_ref(); | ------------ borrow later used here | -help: consider using a `let` binding to create a longer lived value - | -LL ~ let binding = id('y'); -LL ~ { -LL | -LL | let mut v4 = Vec::new(); // (sub) statement 0 -LL | -LL ~ v4.push(&binding); - | + = note: consider using a `let` binding to create a longer lived value error[E0716]: temporary value dropped while borrowed - --> $DIR/borrowck-let-suggestion-suffixes.rs:39:14 + --> $DIR/borrowck-let-suggestion-suffixes.rs:40:14 | LL | v5.push(&id('z')); | ^^^^^^^ - temporary value is freed at the end of this statement From 3e5809d94ff642c4ae9ed80e46d3715fdf2c50c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 14 Jul 2022 18:43:41 -0700 Subject: [PATCH 22/30] Move tests to fit in limit --- src/test/ui/{issues => borrowck}/issue-11493.fixed | 0 src/test/ui/{issues => borrowck}/issue-11493.rs | 0 src/test/ui/{issues => borrowck}/issue-11493.stderr | 0 src/test/ui/{issues => borrowck}/issue-36082.fixed | 0 src/test/ui/{issues => borrowck}/issue-36082.rs | 0 src/test/ui/{issues => borrowck}/issue-36082.stderr | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename src/test/ui/{issues => borrowck}/issue-11493.fixed (100%) rename src/test/ui/{issues => borrowck}/issue-11493.rs (100%) rename src/test/ui/{issues => borrowck}/issue-11493.stderr (100%) rename src/test/ui/{issues => borrowck}/issue-36082.fixed (100%) rename src/test/ui/{issues => borrowck}/issue-36082.rs (100%) rename src/test/ui/{issues => borrowck}/issue-36082.stderr (100%) diff --git a/src/test/ui/issues/issue-11493.fixed b/src/test/ui/borrowck/issue-11493.fixed similarity index 100% rename from src/test/ui/issues/issue-11493.fixed rename to src/test/ui/borrowck/issue-11493.fixed diff --git a/src/test/ui/issues/issue-11493.rs b/src/test/ui/borrowck/issue-11493.rs similarity index 100% rename from src/test/ui/issues/issue-11493.rs rename to src/test/ui/borrowck/issue-11493.rs diff --git a/src/test/ui/issues/issue-11493.stderr b/src/test/ui/borrowck/issue-11493.stderr similarity index 100% rename from src/test/ui/issues/issue-11493.stderr rename to src/test/ui/borrowck/issue-11493.stderr diff --git a/src/test/ui/issues/issue-36082.fixed b/src/test/ui/borrowck/issue-36082.fixed similarity index 100% rename from src/test/ui/issues/issue-36082.fixed rename to src/test/ui/borrowck/issue-36082.fixed diff --git a/src/test/ui/issues/issue-36082.rs b/src/test/ui/borrowck/issue-36082.rs similarity index 100% rename from src/test/ui/issues/issue-36082.rs rename to src/test/ui/borrowck/issue-36082.rs diff --git a/src/test/ui/issues/issue-36082.stderr b/src/test/ui/borrowck/issue-36082.stderr similarity index 100% rename from src/test/ui/issues/issue-36082.stderr rename to src/test/ui/borrowck/issue-36082.stderr From 3ee4e5f7040e2c7bca4dd39a6c5670c1a343eeb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 15 Jul 2022 11:07:20 -0700 Subject: [PATCH 23/30] Fix rebase --- .../rustc_borrowck/src/diagnostics/conflict_errors.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c4eed784b88..bc64cb81179 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -7,7 +7,7 @@ use rustc_errors::{ }; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; @@ -1504,8 +1504,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut suggested = false; let msg = "consider using a `let` binding to create a longer lived value"; - use rustc_hir::intravisit::Visitor; - /// We check that there's a single level of block nesting to ensure always correct /// suggestions. If we don't, then we only provide a free-form message to avoid /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`. @@ -1520,14 +1518,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { impl<'tcx> Visitor<'tcx> for NestedStatementVisitor { fn visit_block(&mut self, block: &hir::Block<'tcx>) { self.current += 1; - rustc_hir::intravisit::walk_block(self, block); + walk_block(self, block); self.current -= 1; } fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) { if self.span == expr.span { self.found = self.current; } - rustc_hir::intravisit::walk_expr(self, expr); + walk_expr(self, expr); } } let source_info = self.body.source_info(location); From aeb949753e80ca741df89612e446a77d1c16e3ab Mon Sep 17 00:00:00 2001 From: yanchith Date: Sat, 16 Jul 2022 11:58:26 +0200 Subject: [PATCH 24/30] Borrow Vec as [T] --- library/alloc/src/slice.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 4a9cecd9b4e..63d4d945290 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -836,14 +836,14 @@ impl> Join<&[T]> for [V] { //////////////////////////////////////////////////////////////////////////////// #[stable(feature = "rust1", since = "1.0.0")] -impl Borrow<[T]> for Vec { +impl Borrow<[T]> for Vec { fn borrow(&self) -> &[T] { &self[..] } } #[stable(feature = "rust1", since = "1.0.0")] -impl BorrowMut<[T]> for Vec { +impl BorrowMut<[T]> for Vec { fn borrow_mut(&mut self) -> &mut [T] { &mut self[..] } From 653a2146326ba0a8698428c93e524ddb382ace67 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 16 Jul 2022 12:11:43 +0200 Subject: [PATCH 25/30] docs: add missing word --- compiler/rustc_typeck/src/check/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index d6160266dd7..849e96445d3 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -25,7 +25,7 @@ can be broken down into several distinct phases: - regionck: after main is complete, the regionck pass goes over all types looking for regions and making sure that they did not escape - into places they are not in scope. This may also influence the + into places where they are not in scope. This may also influence the final assignments of the various region variables if there is some flexibility. From 1a15c7147f90afaa64ae3ff27fcbd678e2e44a8e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 15 Jul 2022 17:37:07 +0200 Subject: [PATCH 26/30] Fix rustdoc JSON inline --- src/librustdoc/clean/mod.rs | 5 +++-- src/librustdoc/json/conversions.rs | 30 ++++++++++++++++++++++++++---- src/librustdoc/json/mod.rs | 9 +++++++++ src/librustdoc/visit_ast.rs | 4 ++++ src/rustdoc-json-types/lib.rs | 5 ++++- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2c98cba90d7..d6260b8ca06 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2120,8 +2120,9 @@ fn clean_use_statement<'tcx>( // forcefully don't inline if this is not public or if the // #[doc(no_inline)] attribute is present. // Don't inline doc(hidden) imports so they can be stripped at a later stage. - let mut denied = !(visibility.is_public() - || (cx.render_options.document_private && is_visible_from_parent_mod)) + let mut denied = cx.output_format.is_json() + || !(visibility.is_public() + || (cx.render_options.document_private && is_visible_from_parent_mod)) || pub_underscore || attrs.iter().any(|a| { a.has_name(sym::doc) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index db2ad953f6a..2598b9b0b28 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -43,7 +43,16 @@ impl JsonRenderer<'_> { let span = item.span(self.tcx); let clean::Item { name, attrs: _, kind: _, visibility, item_id, cfg: _ } = item; let inner = match *item.kind { - clean::StrippedItem(_) | clean::KeywordItem(_) => return None, + clean::KeywordItem(_) => return None, + clean::StrippedItem(ref inner) => { + match &**inner { + // We document non-empty stripped modules as with `Module::is_stripped` set to + // `true`, to prevent contained items from being orphaned for downstream users, + // as JSON does no inlining. + clean::ModuleItem(m) if !m.items.is_empty() => from_clean_item(item, self.tcx), + _ => return None, + } + } _ => from_clean_item(item, self.tcx), }; Some(Item { @@ -220,7 +229,9 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { let header = item.fn_header(tcx); match *item.kind { - ModuleItem(m) => ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx) }), + ModuleItem(m) => { + ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false }) + } ImportItem(i) => ItemEnum::Import(i.into_tcx(tcx)), StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)), UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)), @@ -257,8 +268,19 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { bounds: b.into_iter().map(|x| x.into_tcx(tcx)).collect(), default: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)), }, - // `convert_item` early returns `None` for striped items and keywords. - StrippedItem(_) | KeywordItem(_) => unreachable!(), + // `convert_item` early returns `None` for stripped items and keywords. + KeywordItem(_) => unreachable!(), + StrippedItem(inner) => { + match *inner { + ModuleItem(m) => ItemEnum::Module(Module { + is_crate, + items: ids(m.items, tcx), + is_stripped: true, + }), + // `convert_item` early returns `None` for stripped items we're not including + _ => unreachable!(), + } + } ExternCrateItem { ref src } => ItemEnum::ExternCrate { name: name.as_ref().unwrap().to_string(), rename: src.map(|x| x.to_string()), diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index c7251b51152..6364d00d062 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -21,6 +21,7 @@ use rustc_span::def_id::LOCAL_CRATE; use rustdoc_json_types as types; use crate::clean::types::{ExternalCrate, ExternalLocation}; +use crate::clean::ItemKind; use crate::config::RenderOptions; use crate::docfs::PathError; use crate::error::Error; @@ -175,6 +176,14 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { /// the hashmap because certain items (traits and types) need to have their mappings for trait /// implementations filled out before they're inserted. fn item(&mut self, item: clean::Item) -> Result<(), Error> { + trace!("rendering {} {:?}", item.type_(), item.name); + + // Flatten items that recursively store other items. We include orphaned items from + // stripped modules and etc that are otherwise reachable. + if let ItemKind::StrippedItem(inner) = &*item.kind { + inner.inner_items().for_each(|i| self.item(i.clone()).unwrap()); + } + // Flatten items that recursively store other items item.kind.inner_items().for_each(|i| self.item(i.clone()).unwrap()); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index ac934f6925d..ca7a20bf368 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -190,6 +190,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { ) -> bool { debug!("maybe_inline_local res: {:?}", res); + if self.cx.output_format.is_json() { + return false; + } + let tcx = self.cx.tcx; let Some(res_did) = res.opt_def_id() else { return false; diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 1168a89a8b2..761e94c7ebb 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; /// rustdoc format-version. -pub const FORMAT_VERSION: u32 = 15; +pub const FORMAT_VERSION: u32 = 16; /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information /// about the language items in the local crate, as well as info about external items to allow @@ -245,6 +245,9 @@ pub enum ItemEnum { pub struct Module { pub is_crate: bool, pub items: Vec, + /// If `true`, this module is not part of the public API, but it contains + /// items that are re-exported as public API. + pub is_stripped: bool, } #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] From 3c55a26cb489abd4c8e8dc4835bad77ec80be493 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 15 Jul 2022 18:10:52 +0200 Subject: [PATCH 27/30] Correctly handle usage of private items in public API for JSON output format --- src/librustdoc/core.rs | 3 +++ src/librustdoc/passes/strip_private.rs | 1 + src/librustdoc/passes/stripper.rs | 22 ++++++++++++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index a658e78bf60..0e9a9e0e506 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -81,6 +81,8 @@ pub(crate) struct DocContext<'tcx> { pub(crate) inlined: FxHashSet, /// Used by `calculate_doc_coverage`. pub(crate) output_format: OutputFormat, + /// Used by `strip_private`. + pub(crate) show_coverage: bool, } impl<'tcx> DocContext<'tcx> { @@ -381,6 +383,7 @@ pub(crate) fn run_global_ctxt( inlined: FxHashSet::default(), output_format, render_options, + show_coverage, }; // Small hack to force the Sized trait to be present. diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 6c94912bc53..9ba841a31cf 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -24,6 +24,7 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> retained: &mut retained, access_levels: &cx.cache.access_levels, update_retained: true, + is_json_output: cx.output_format.is_json() && !cx.show_coverage, }; krate = ImportStripper.fold_crate(stripper.fold_crate(krate)); } diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs index 0fd124e6154..5f2f50e712b 100644 --- a/src/librustdoc/passes/stripper.rs +++ b/src/librustdoc/passes/stripper.rs @@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::middle::privacy::AccessLevels; use std::mem; -use crate::clean::{self, Item, ItemIdSet}; +use crate::clean::{self, Item, ItemId, ItemIdSet}; use crate::fold::{strip_item, DocFolder}; use crate::formats::cache::Cache; @@ -11,6 +11,21 @@ pub(crate) struct Stripper<'a> { pub(crate) retained: &'a mut ItemIdSet, pub(crate) access_levels: &'a AccessLevels, pub(crate) update_retained: bool, + pub(crate) is_json_output: bool, +} + +impl<'a> Stripper<'a> { + // We need to handle this differently for the JSON output because some non exported items could + // be used in public API. And so, we need these items as well. `is_exported` only checks if they + // are in the public API, which is not enough. + #[inline] + fn is_item_reachable(&self, item_id: ItemId) -> bool { + if self.is_json_output { + self.access_levels.is_reachable(item_id.expect_def_id()) + } else { + self.access_levels.is_exported(item_id.expect_def_id()) + } + } } impl<'a> DocFolder for Stripper<'a> { @@ -45,9 +60,8 @@ impl<'a> DocFolder for Stripper<'a> { | clean::TraitAliasItem(..) | clean::MacroItem(..) | clean::ForeignTypeItem => { - if i.item_id.is_local() - && !self.access_levels.is_exported(i.item_id.expect_def_id()) - { + let item_id = i.item_id; + if item_id.is_local() && !self.is_item_reachable(item_id) { debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name); return None; } From b95b1389c50f000683c7c2fc5f8713cb5d3bfa96 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 15 Jul 2022 17:48:46 +0200 Subject: [PATCH 28/30] Add tests for JSON non-inlining --- src/test/rustdoc-json/doc_hidden_failure.rs | 22 +++++++++++++++++++ .../reexport/auxiliary/pub-struct.rs | 1 + src/test/rustdoc-json/reexport/glob_extern.rs | 7 +++--- .../rustdoc-json/reexport/glob_private.rs | 15 ++++++++----- .../rustdoc-json/reexport/in_root_and_mod.rs | 8 ++++--- .../reexport/private_twice_one_inline.rs | 18 +++++++++++++++ .../reexport/private_two_names.rs | 17 ++++++++++++++ .../rustdoc-json/reexport/rename_private.rs | 10 ++++----- .../same_type_reexported_more_than_once.rs | 8 +++---- .../rustdoc-json/reexport/simple_private.rs | 8 ++++--- src/test/rustdoc-json/return_private.rs | 15 +++++++++++++ src/test/rustdoc-json/stripped_modules.rs | 21 ++++++++++++++++++ 12 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 src/test/rustdoc-json/doc_hidden_failure.rs create mode 100644 src/test/rustdoc-json/reexport/auxiliary/pub-struct.rs create mode 100644 src/test/rustdoc-json/reexport/private_twice_one_inline.rs create mode 100644 src/test/rustdoc-json/reexport/private_two_names.rs create mode 100644 src/test/rustdoc-json/return_private.rs create mode 100644 src/test/rustdoc-json/stripped_modules.rs diff --git a/src/test/rustdoc-json/doc_hidden_failure.rs b/src/test/rustdoc-json/doc_hidden_failure.rs new file mode 100644 index 00000000000..5c4ccf996a5 --- /dev/null +++ b/src/test/rustdoc-json/doc_hidden_failure.rs @@ -0,0 +1,22 @@ +// Regression test for . + +#![feature(no_core)] +#![no_core] + +mod auto { + mod action_row { + pub struct ActionRowBuilder; + } + + #[doc(hidden)] + pub mod builders { + pub use super::action_row::ActionRowBuilder; + } +} + +// @count doc_hidden_failure.json "$.index[*][?(@.name=='builders')]" 2 +pub use auto::*; + +pub mod builders { + pub use crate::auto::builders::*; +} diff --git a/src/test/rustdoc-json/reexport/auxiliary/pub-struct.rs b/src/test/rustdoc-json/reexport/auxiliary/pub-struct.rs new file mode 100644 index 00000000000..4a835673a59 --- /dev/null +++ b/src/test/rustdoc-json/reexport/auxiliary/pub-struct.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/src/test/rustdoc-json/reexport/glob_extern.rs b/src/test/rustdoc-json/reexport/glob_extern.rs index 831c185f6b1..ba1cfd8a0b5 100644 --- a/src/test/rustdoc-json/reexport/glob_extern.rs +++ b/src/test/rustdoc-json/reexport/glob_extern.rs @@ -3,15 +3,16 @@ #![no_core] #![feature(no_core)] -// @!has glob_extern.json "$.index[*][?(@.name=='mod1')]" +// @is glob_extern.json "$.index[*][?(@.name=='mod1')].kind" \"module\" +// @is glob_extern.json "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true" mod mod1 { extern "C" { - // @set public_fn_id = - "$.index[*][?(@.name=='public_fn')].id" + // @has - "$.index[*][?(@.name=='public_fn')].id" pub fn public_fn(); // @!has - "$.index[*][?(@.name=='private_fn')]" fn private_fn(); } } -// @has - "$.index[*][?(@.name=='glob_extern')].inner.items[*]" $public_fn_id +// @is - "$.index[*][?(@.kind=='import')].inner.glob" true pub use mod1::*; diff --git a/src/test/rustdoc-json/reexport/glob_private.rs b/src/test/rustdoc-json/reexport/glob_private.rs index e907de92367..e6a44748c25 100644 --- a/src/test/rustdoc-json/reexport/glob_private.rs +++ b/src/test/rustdoc-json/reexport/glob_private.rs @@ -3,9 +3,11 @@ #![no_core] #![feature(no_core)] -// @!has glob_private.json "$.index[*][?(@.name=='mod1')]" +// @is glob_private.json "$.index[*][?(@.name=='mod1')].kind" \"module\" +// @is glob_private.json "$.index[*][?(@.name=='mod1')].inner.is_stripped" "true" mod mod1 { - // @!has - "$.index[*][?(@.name=='mod2')]" + // @is - "$.index[*][?(@.name=='mod2')].kind" \"module\" + // @is - "$.index[*][?(@.name=='mod2')].inner.is_stripped" "true" mod mod2 { // @set m2pub_id = - "$.index[*][?(@.name=='Mod2Public')].id" pub struct Mod2Public; @@ -13,15 +15,18 @@ mod mod1 { // @!has - "$.index[*][?(@.name=='Mod2Private')]" struct Mod2Private; } + + // @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod2')]" pub use self::mod2::*; // @set m1pub_id = - "$.index[*][?(@.name=='Mod1Public')].id" pub struct Mod1Public; - // @!has - "$.index[*][?(@.name=='Mod1Private')]" struct Mod1Private; } + +// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='mod1')]" pub use mod1::*; -// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m2pub_id -// @has - "$.index[*][?(@.name=='glob_private')].inner.items[*]" $m1pub_id +// @has - "$.index[*][?(@.name=='mod2')].inner.items[*]" $m2pub_id +// @has - "$.index[*][?(@.name=='mod1')].inner.items[*]" $m1pub_id diff --git a/src/test/rustdoc-json/reexport/in_root_and_mod.rs b/src/test/rustdoc-json/reexport/in_root_and_mod.rs index e3cecbdd7ff..7bf10a98686 100644 --- a/src/test/rustdoc-json/reexport/in_root_and_mod.rs +++ b/src/test/rustdoc-json/reexport/in_root_and_mod.rs @@ -1,15 +1,17 @@ #![feature(no_core)] #![no_core] +// @is in_root_and_mod.json "$.index[*][?(@.name=='foo')].kind" \"module\" +// @is in_root_and_mod.json "$.index[*][?(@.name=='foo')].inner.is_stripped" "true" mod foo { - // @set foo_id = in_root_and_mod.json "$.index[*][?(@.name=='Foo')].id" + // @has - "$.index[*][?(@.name=='Foo')]" pub struct Foo; } -// @has - "$.index[*][?(@.name=='in_root_and_mod')].inner.items[*]" $foo_id +// @has - "$.index[*][?(@.kind=='import' && @.inner.source=='foo::Foo')]" pub use foo::Foo; pub mod bar { - // @has - "$.index[*][?(@.name=='bar')].inner.items[*]" $foo_id + // @has - "$.index[*][?(@.kind=='import' && @.inner.source=='crate::foo::Foo')]" pub use crate::foo::Foo; } diff --git a/src/test/rustdoc-json/reexport/private_twice_one_inline.rs b/src/test/rustdoc-json/reexport/private_twice_one_inline.rs new file mode 100644 index 00000000000..327b0f45fdd --- /dev/null +++ b/src/test/rustdoc-json/reexport/private_twice_one_inline.rs @@ -0,0 +1,18 @@ +// aux-build:pub-struct.rs + +// Test for the ICE in rust/83057 +// Am external type re-exported with different attributes shouldn't cause an error + +#![no_core] +#![feature(no_core)] + +extern crate pub_struct as foo; + +#[doc(inline)] +pub use foo::Foo; + +pub mod bar { + pub use foo::Foo; +} + +// @count private_twice_one_inline.json "$.index[*][?(@.kind=='import')]" 2 diff --git a/src/test/rustdoc-json/reexport/private_two_names.rs b/src/test/rustdoc-json/reexport/private_two_names.rs new file mode 100644 index 00000000000..36d6a50d385 --- /dev/null +++ b/src/test/rustdoc-json/reexport/private_two_names.rs @@ -0,0 +1,17 @@ +// Test for the ICE in rust/83720 +// A pub-in-private type re-exported under two different names shouldn't cause an error + +#![no_core] +#![feature(no_core)] + +// @is private_two_names.json "$.index[*][?(@.name=='style')].kind" \"module\" +// @is private_two_names.json "$.index[*][?(@.name=='style')].inner.is_stripped" "true" +mod style { + // @has - "$.index[*](?(@.name=='Color'))" + pub struct Color; +} + +// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='Color')]" +pub use style::Color; +// @has - "$.index[*][?(@.kind=='import' && @.inner.name=='Colour')]" +pub use style::Color as Colour; diff --git a/src/test/rustdoc-json/reexport/rename_private.rs b/src/test/rustdoc-json/reexport/rename_private.rs index fb8296f2337..2476399bd56 100644 --- a/src/test/rustdoc-json/reexport/rename_private.rs +++ b/src/test/rustdoc-json/reexport/rename_private.rs @@ -2,13 +2,13 @@ #![no_core] #![feature(no_core)] -// @!has rename_private.json "$.index[*][?(@.name=='inner')]" + +// @is rename_private.json "$.index[*][?(@.name=='inner')].kind" \"module\" +// @is rename_private.json "$.index[*][?(@.name=='inner')].inner.is_stripped" "true" mod inner { - // @!has - "$.index[*][?(@.name=='Public')]" + // @has - "$.index[*][?(@.name=='Public')]" pub struct Public; } -// @set newname_id = - "$.index[*][?(@.name=='NewName')].id" -// @is - "$.index[*][?(@.name=='NewName')].kind" \"struct\" -// @has - "$.index[*][?(@.name=='rename_private')].inner.items[*]" $newname_id +// @is - "$.index[*][?(@.kind=='import')].inner.name" \"NewName\" pub use inner::Public as NewName; diff --git a/src/test/rustdoc-json/reexport/same_type_reexported_more_than_once.rs b/src/test/rustdoc-json/reexport/same_type_reexported_more_than_once.rs index fd6ac8372d9..eedddd6a7bb 100644 --- a/src/test/rustdoc-json/reexport/same_type_reexported_more_than_once.rs +++ b/src/test/rustdoc-json/reexport/same_type_reexported_more_than_once.rs @@ -1,15 +1,13 @@ -// Regression test for https://github.com/rust-lang/rust/issues/97432. +// Regression test for . #![feature(no_core)] #![no_std] #![no_core] // @has same_type_reexported_more_than_once.json -// @set trait_id = - "$.index[*][?(@.name=='Trait')].id" -// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $trait_id +// @has - "$.index[*][?(@.name=='Trait')]" pub use inner::Trait; -// @set reexport_id = - "$.index[*][?(@.name=='Reexport')].id" -// @has - "$.index[*][?(@.name=='same_type_reexported_more_than_once')].inner.items[*]" $reexport_id +// @has - "$.index[*].inner[?(@.name=='Reexport')].id" pub use inner::Trait as Reexport; mod inner { diff --git a/src/test/rustdoc-json/reexport/simple_private.rs b/src/test/rustdoc-json/reexport/simple_private.rs index 658b121e6ce..5ec13e403ae 100644 --- a/src/test/rustdoc-json/reexport/simple_private.rs +++ b/src/test/rustdoc-json/reexport/simple_private.rs @@ -1,13 +1,15 @@ // edition:2018 - #![no_core] #![feature(no_core)] -// @!has simple_private.json "$.index[*][?(@.name=='inner')]" +// @is simple_private.json "$.index[*][?(@.name=='inner')].kind" \"module\" +// @is simple_private.json "$.index[*][?(@.name=='inner')].inner.is_stripped" "true" mod inner { // @set pub_id = - "$.index[*][?(@.name=='Public')].id" pub struct Public; } -// @has - "$.index[*][?(@.name=='simple_private')].inner.items[*]" $pub_id +// @is - "$.index[*][?(@.kind=='import')].inner.name" \"Public\" pub use inner::Public; + +// @has - "$.index[*][?(@.name=='inner')].inner.items[*]" $pub_id diff --git a/src/test/rustdoc-json/return_private.rs b/src/test/rustdoc-json/return_private.rs new file mode 100644 index 00000000000..6b324d0090a --- /dev/null +++ b/src/test/rustdoc-json/return_private.rs @@ -0,0 +1,15 @@ +// Regression test for . +// ignore-tidy-linelength + +#![feature(no_core)] +#![no_core] + +mod secret { + pub struct Secret; +} + +// @is return_private.json "$.index[*][?(@.name=='get_secret')].kind" \"function\" +// @is return_private.json "$.index[*][?(@.name=='get_secret')].inner.decl.output.inner.name" \"secret::Secret\" +pub fn get_secret() -> secret::Secret { + secret::Secret +} diff --git a/src/test/rustdoc-json/stripped_modules.rs b/src/test/rustdoc-json/stripped_modules.rs new file mode 100644 index 00000000000..91f9f02ad7b --- /dev/null +++ b/src/test/rustdoc-json/stripped_modules.rs @@ -0,0 +1,21 @@ +#![no_core] +#![feature(no_core)] + +// @!has stripped_modules.json "$.index[*][?(@.name=='no_pub_inner')]" +mod no_pub_inner { + fn priv_inner() {} +} + +// @!has - "$.index[*][?(@.name=='pub_inner_unreachable')]" +mod pub_inner_unreachable { + // @!has - "$.index[*][?(@.name=='pub_inner_1')]" + pub fn pub_inner_1() {} +} + +// @has - "$.index[*][?(@.name=='pub_inner_reachable')]" +mod pub_inner_reachable { + // @has - "$.index[*][?(@.name=='pub_inner_2')]" + pub fn pub_inner_2() {} +} + +pub use pub_inner_reachable::pub_inner_2; From b393e979dc8edf1807b19e0619c617eb786012c6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 16 Jul 2022 13:53:43 +0200 Subject: [PATCH 29/30] Fix flakyness of GUI tests --- src/test/rustdoc-gui/escape-key.goml | 2 ++ src/test/rustdoc-gui/search-filter.goml | 2 ++ src/test/rustdoc-gui/search-reexport.goml | 4 ++++ src/test/rustdoc-gui/search-result-color.goml | 2 ++ src/test/rustdoc-gui/search-result-display.goml | 2 ++ src/test/rustdoc-gui/search-result-keyword.goml | 2 ++ .../rustdoc-gui/search-tab-change-title-fn-sig.goml | 10 ++++++++++ src/test/rustdoc-gui/settings.goml | 2 ++ 8 files changed, 26 insertions(+) diff --git a/src/test/rustdoc-gui/escape-key.goml b/src/test/rustdoc-gui/escape-key.goml index d083b0ae0c9..a5afb037d0f 100644 --- a/src/test/rustdoc-gui/escape-key.goml +++ b/src/test/rustdoc-gui/escape-key.goml @@ -3,6 +3,8 @@ goto: file://|DOC_PATH|/test_docs/index.html // First, we check that the search results are hidden when the Escape key is pressed. write: (".search-input", "test") +// To be SURE that the search will be run. +press-key: 'Enter' wait-for: "#search h1" // The search element is empty before the first search // Check that the currently displayed element is search. wait-for: "#alternative-display #search" diff --git a/src/test/rustdoc-gui/search-filter.goml b/src/test/rustdoc-gui/search-filter.goml index d0b3175114c..d645e237061 100644 --- a/src/test/rustdoc-gui/search-filter.goml +++ b/src/test/rustdoc-gui/search-filter.goml @@ -2,6 +2,8 @@ goto: file://|DOC_PATH|/test_docs/index.html show-text: true write: (".search-input", "test") +// To be SURE that the search will be run. +press-key: 'Enter' // Waiting for the search results to appear... wait-for: "#titles" assert-text: ("#results .externcrate", "test_docs") diff --git a/src/test/rustdoc-gui/search-reexport.goml b/src/test/rustdoc-gui/search-reexport.goml index 557781d4810..5ef890d472b 100644 --- a/src/test/rustdoc-gui/search-reexport.goml +++ b/src/test/rustdoc-gui/search-reexport.goml @@ -7,6 +7,8 @@ reload: assert-text: ("//*[@id='reexport.TheStdReexport']", "pub use ::std as TheStdReexport;") assert-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgba(0, 0, 0, 0)"}) write: (".search-input", "TheStdReexport") +// To be SURE that the search will be run. +press-key: 'Enter' wait-for: "//a[@class='result-import']" assert-attribute: ( "//a[@class='result-import']", @@ -18,6 +20,8 @@ click: "//a[@class='result-import']" wait-for-css: ("//*[@id='reexport.TheStdReexport']", {"background-color": "rgb(73, 74, 61)"}) // We now check that the alias is working as well on the reexport. +// To be SURE that the search will be run. +press-key: 'Enter' write: (".search-input", "AliasForTheStdReexport") wait-for: "//a[@class='result-import']" assert-text: ( diff --git a/src/test/rustdoc-gui/search-result-color.goml b/src/test/rustdoc-gui/search-result-color.goml index 901634fe0e6..9a49ae2c6b8 100644 --- a/src/test/rustdoc-gui/search-result-color.goml +++ b/src/test/rustdoc-gui/search-result-color.goml @@ -89,6 +89,8 @@ show-text: true // We reload the page so the local storage settings are being used. reload: write: (".search-input", "thisisanalias") +// To be SURE that the search will be run. +press-key: 'Enter' // Waiting for the search results to appear... wait-for: "#titles" // Checking that the colors for the alias element are the ones expected. diff --git a/src/test/rustdoc-gui/search-result-display.goml b/src/test/rustdoc-gui/search-result-display.goml index b31905a126a..6295d7fae89 100644 --- a/src/test/rustdoc-gui/search-result-display.goml +++ b/src/test/rustdoc-gui/search-result-display.goml @@ -2,6 +2,8 @@ goto: file://|DOC_PATH|/test_docs/index.html size: (900, 1000) write: (".search-input", "test") +// To be SURE that the search will be run. +press-key: 'Enter' wait-for: "#search-settings" // The width is returned by "getComputedStyle" which returns the exact number instead of the // CSS rule which is "50%"... diff --git a/src/test/rustdoc-gui/search-result-keyword.goml b/src/test/rustdoc-gui/search-result-keyword.goml index 8b50c5c5e1a..16ae10431ac 100644 --- a/src/test/rustdoc-gui/search-result-keyword.goml +++ b/src/test/rustdoc-gui/search-result-keyword.goml @@ -1,6 +1,8 @@ // Checks that the "keyword" results have the expected text alongside them. goto: file://|DOC_PATH|/test_docs/index.html write: (".search-input", "CookieMonster") +// To be SURE that the search will be run. +press-key: 'Enter' // Waiting for the search results to appear... wait-for: "#titles" // Note: The two next assert commands could be merged as one but readability would be diff --git a/src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml b/src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml index 763927f9d0f..9d506c1519e 100644 --- a/src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml +++ b/src/test/rustdoc-gui/search-tab-change-title-fn-sig.goml @@ -2,6 +2,8 @@ // First, try a search-by-name goto: file://|DOC_PATH|/test_docs/index.html write: (".search-input", "Foo") +// To be SURE that the search will be run. +press-key: 'Enter' // Waiting for the search results to appear... wait-for: "#titles" assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) @@ -22,6 +24,8 @@ wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"}) // Now try search-by-return goto: file://|DOC_PATH|/test_docs/index.html write: (".search-input", "-> String") +// To be SURE that the search will be run. +press-key: 'Enter' // Waiting for the search results to appear... wait-for: "#titles" assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) @@ -42,6 +46,8 @@ wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) // Try with a search-by-return with no results goto: file://|DOC_PATH|/test_docs/index.html write: (".search-input", "-> Something") +// To be SURE that the search will be run. +press-key: 'Enter' // Waiting for the search results to appear... wait-for: "#titles" assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) @@ -50,6 +56,8 @@ assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STA // Try with a search-by-parameter goto: file://|DOC_PATH|/test_docs/index.html write: (".search-input", "usize pattern") +// To be SURE that the search will be run. +press-key: 'Enter' // Waiting for the search results to appear... wait-for: "#titles" assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) @@ -58,6 +66,8 @@ assert-text: ("#titles > button:nth-of-type(1)", "In Function Parameters", START // Try with a search-by-parameter-and-return goto: file://|DOC_PATH|/test_docs/index.html write: (".search-input", "pattern -> str") +// To be SURE that the search will be run. +press-key: 'Enter' // Waiting for the search results to appear... wait-for: "#titles" assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"}) diff --git a/src/test/rustdoc-gui/settings.goml b/src/test/rustdoc-gui/settings.goml index c402c7991c8..8a3365d3cc2 100644 --- a/src/test/rustdoc-gui/settings.goml +++ b/src/test/rustdoc-gui/settings.goml @@ -20,6 +20,8 @@ wait-for-css: ("#settings", {"display": "none"}) // Let's click on it when the search results are displayed. focus: ".search-input" write: "test" +// To be SURE that the search will be run. +press-key: 'Enter' wait-for: "#alternative-display #search" click: "#settings-menu" wait-for-css: ("#settings", {"display": "block"}) From c54d4ada26c6a92346076bcc27d628398345ed9e Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sun, 17 Jul 2022 04:09:20 +0900 Subject: [PATCH 30/30] avoid some `Symbol` to `String` conversions --- .../src/diagnostics/conflict_errors.rs | 7 +++---- .../src/diagnostics/explain_borrow.rs | 4 ++-- .../src/diagnostics/region_errors.rs | 7 +++---- .../src/diagnostics/region_name.rs | 17 +++++++---------- compiler/rustc_errors/src/diagnostic.rs | 2 +- compiler/rustc_expand/src/module.rs | 5 ++--- .../src/infer/error_reporting/need_type_info.rs | 4 ++-- compiler/rustc_middle/src/ty/layout.rs | 4 ++-- compiler/rustc_middle/src/ty/print/pretty.rs | 12 ++++++------ compiler/rustc_session/src/code_stats.rs | 7 ++++--- compiler/rustc_typeck/src/astconv/mod.rs | 2 +- .../rustc_typeck/src/check/compare_method.rs | 6 +++--- compiler/rustc_typeck/src/check/expr.rs | 2 +- .../rustc_typeck/src/check/method/suggest.rs | 9 +++++---- compiler/rustc_typeck/src/impl_wf_check.rs | 15 +++++---------- src/tools/clippy/clippy_lints/src/format.rs | 2 +- .../clippy_lints/src/inherent_to_string.rs | 2 +- .../src/methods/unnecessary_to_owned.rs | 2 +- 18 files changed, 50 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index b9cfc3732dc..146f86e5a11 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -23,7 +23,7 @@ use rustc_middle::ty::{ use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::sym; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::TraitEngineExt as _; @@ -1227,8 +1227,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { from_closure: false, region_name: RegionName { - source: - RegionNameSource::AnonRegionFromUpvar(upvar_span, ref upvar_name), + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), .. }, span, @@ -1702,7 +1701,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { borrow_span: Span, name: &Option, upvar_span: Span, - upvar_name: &str, + upvar_name: Symbol, escape_span: Span, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { let tcx = self.infcx.tcx; diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 230ccf51990..72aee0267ac 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -12,7 +12,7 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::{self, RegionVid, TyCtxt}; -use rustc_span::symbol::Symbol; +use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, DesugaringKind, Span}; use crate::region_infer::BlameConstraint; @@ -282,7 +282,7 @@ impl<'tcx> BorrowExplanation<'tcx> { ) { if let ConstraintCategory::OpaqueType = category { let suggestable_name = - if region_name.was_named() { region_name.to_string() } else { "'_".to_string() }; + if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime }; let msg = format!( "you can add a bound to the {}to make it last less than `'static` and match `{}`", diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 5d3997289bb..7b2f771288f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -19,8 +19,7 @@ use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::Region; use rustc_middle::ty::TypeVisitor; use rustc_middle::ty::{self, RegionVid, Ty}; -use rustc_span::symbol::sym; -use rustc_span::symbol::Ident; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; use crate::borrowck_errors; @@ -758,7 +757,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { return; }; - let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() }; + let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime }; let arg = match param.param.pat.simple_ident() { Some(simple_ident) => format!("argument `{}`", simple_ident), @@ -770,7 +769,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.infcx.tcx, diag, fn_returns, - lifetime, + lifetime.to_string(), Some(arg), captures, Some((param.param_ty_span, param.param_ty.to_string())), diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index e60e11f11df..49f8218b222 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -34,13 +34,13 @@ pub(crate) enum RegionNameSource { /// The `'static` region. Static, /// The free region corresponding to the environment of a closure. - SynthesizedFreeEnvRegion(Span, String), + SynthesizedFreeEnvRegion(Span, &'static str), /// The region corresponding to an argument. AnonRegionFromArgument(RegionNameHighlight), /// The region corresponding to a closure upvar. - AnonRegionFromUpvar(Span, String), + AnonRegionFromUpvar(Span, Symbol), /// The region corresponding to the return type of a closure. - AnonRegionFromOutput(RegionNameHighlight, String), + AnonRegionFromOutput(RegionNameHighlight, &'static str), /// The region from a type yielded by a generator. AnonRegionFromYieldTy(Span, String), /// An anonymous region from an async fn. @@ -110,7 +110,7 @@ impl RegionName { } RegionNameSource::SynthesizedFreeEnvRegion(span, note) => { diag.span_label(*span, format!("lifetime `{self}` represents this closure's body")); - diag.note(note); + diag.note(*note); } RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy( span, @@ -350,10 +350,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { Some(RegionName { name: region_name, - source: RegionNameSource::SynthesizedFreeEnvRegion( - fn_decl_span, - note.to_string(), - ), + source: RegionNameSource::SynthesizedFreeEnvRegion(fn_decl_span, note), }) } @@ -678,7 +675,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { Some(RegionName { name: region_name, - source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name.to_string()), + source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), }) } @@ -756,7 +753,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { Some(RegionName { name: self.synthesize_region_name(), - source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description.to_string()), + source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description), }) } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 9429ad1a897..449aaac2c7b 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -496,7 +496,7 @@ impl Diagnostic { self } - pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self { + pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self { self.highlighted_note(vec![ (format!("`{}` from trait: `", name), Style::NoStyle), (signature, Style::Highlight), diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 876faad33b6..0315d11634c 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -218,10 +218,9 @@ pub fn default_submod_path<'a>( "" }; - let mod_name = ident.name.to_string(); - let default_path_str = format!("{}{}.rs", relative_prefix, mod_name); + let default_path_str = format!("{}{}.rs", relative_prefix, ident.name); let secondary_path_str = - format!("{}{}{}mod.rs", relative_prefix, mod_name, path::MAIN_SEPARATOR); + format!("{}{}{}mod.rs", relative_prefix, ident.name, path::MAIN_SEPARATOR); let default_path = dir_path.join(&default_path_str); let secondary_path = dir_path.join(&secondary_path_str); let default_exists = sess.source_map().file_exists(&default_path); diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 4d29fc46946..a8e71673c21 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -138,7 +138,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind { - Some(name.to_string()) + Some(name) } else { None } @@ -151,7 +151,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPr if let ConstVariableOriginKind::ConstParameterDefinition(name, _) = infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind { - return Some(name.to_string()); + return Some(name); } else { None } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index f87b6e4212d..a009f143198 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1958,7 +1958,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { min_size = field_end; } FieldInfo { - name: name.to_string(), + name, offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), @@ -1967,7 +1967,7 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { .collect(); VariantInfo { - name: n.map(|n| n.to_string()), + name: n, kind: if layout.is_unsized() { SizeKind::Min } else { SizeKind::Exact }, align: layout.align.abi.bytes(), size: if min_size.bytes() == 0 { layout.size.bytes() } else { min_size.bytes() }, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f721a175c98..14eb7c89b74 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1030,11 +1030,11 @@ pub trait PrettyPrinter<'tcx>: } } - fn ty_infer_name(&self, _: ty::TyVid) -> Option { + fn ty_infer_name(&self, _: ty::TyVid) -> Option { None } - fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option { + fn const_infer_name(&self, _: ty::ConstVid<'tcx>) -> Option { None } @@ -1550,8 +1550,8 @@ pub struct FmtPrinterData<'a, 'tcx> { pub region_highlight_mode: RegionHighlightMode<'tcx>, - pub ty_infer_name_resolver: Option Option + 'a>>, - pub const_infer_name_resolver: Option) -> Option + 'a>>, + pub ty_infer_name_resolver: Option Option + 'a>>, + pub const_infer_name_resolver: Option) -> Option + 'a>>, } impl<'a, 'tcx> Deref for FmtPrinter<'a, 'tcx> { @@ -1841,11 +1841,11 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { } impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { - fn ty_infer_name(&self, id: ty::TyVid) -> Option { + fn ty_infer_name(&self, id: ty::TyVid) -> Option { self.0.ty_infer_name_resolver.as_ref().and_then(|func| func(id)) } - fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option { + fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option { self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id)) } diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index cbb03ffd7a0..eede4d16ea3 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -1,11 +1,12 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lock; +use rustc_span::Symbol; use rustc_target::abi::{Align, Size}; use std::cmp::{self, Ordering}; #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct VariantInfo { - pub name: Option, + pub name: Option, pub kind: SizeKind, pub size: u64, pub align: u64, @@ -20,7 +21,7 @@ pub enum SizeKind { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct FieldInfo { - pub name: String, + pub name: Symbol, pub offset: u64, pub size: u64, pub align: u64, @@ -119,7 +120,7 @@ impl CodeStats { let VariantInfo { ref name, kind: _, align: _, size, ref fields } = *variant_info; let indent = if !struct_like { let name = match name.as_ref() { - Some(name) => name.to_owned(), + Some(name) => name.to_string(), None => i.to_string(), }; println!( diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 0a2b54eec47..a2ade1d2731 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -2489,7 +2489,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { concrete type's name `{type_name}` instead if you want to \ specify its type parameters" ), - type_name.to_string(), + type_name, Applicability::MaybeIncorrect, ); } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 2bfb9343877..940163a2998 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -546,7 +546,7 @@ fn compare_self_type<'tcx>( if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { err.span_label(span, format!("trait method declared without `{self_descr}`")); } else { - err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); } let reported = err.emit(); return Err(reported); @@ -566,7 +566,7 @@ fn compare_self_type<'tcx>( if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) { err.span_label(span, format!("`{self_descr}` used in trait")); } else { - err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); } let reported = err.emit(); return Err(reported); @@ -805,7 +805,7 @@ fn compare_number_of_method_arguments<'tcx>( ), ); } else { - err.note_trait_signature(trait_m.name.to_string(), trait_m.signature(tcx)); + err.note_trait_signature(trait_m.name, trait_m.signature(tcx)); } err.span_label( impl_span, diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 45ea04f2342..6dccb4ae8d6 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -1873,7 +1873,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let remaining_private_fields_len = remaining_private_fields.len(); let names = match &remaining_private_fields .iter() - .map(|(name, _, _)| name.to_string()) + .map(|(name, _, _)| name) .collect::>()[..] { _ if remaining_private_fields_len > 6 => String::new(), diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 7bf167426f7..7f96e421a9a 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -19,6 +19,7 @@ use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::ToPolyTraitRef; use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::Symbol; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -1548,7 +1549,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Option>, )], ) { - let mut derives = Vec::<(String, Span, String)>::new(); + let mut derives = Vec::<(String, Span, Symbol)>::new(); let mut traits = Vec::::new(); for (pred, _, _) in unsatisfied_predicates { let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue }; @@ -1581,12 +1582,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { derives.push(( self_name.clone(), self_span, - parent_diagnostic_name.to_string(), + parent_diagnostic_name, )); } } } - derives.push((self_name, self_span, diagnostic_name.to_string())); + derives.push((self_name, self_span, diagnostic_name)); } else { traits.push(self.tcx.def_span(trait_pred.def_id())); } @@ -1609,7 +1610,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { continue; } } - derives_grouped.push((self_name, self_span, trait_name)); + derives_grouped.push((self_name, self_span, trait_name.to_string())); } let len = traits.len(); diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs index e7ca70de4ba..8c26c96816d 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_typeck/src/impl_wf_check.rs @@ -17,7 +17,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; use std::collections::hash_map::Entry::{Occupied, Vacant}; @@ -123,12 +123,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) ty::GenericParamDefKind::Type { .. } => { let param_ty = ty::ParamTy::for_def(param); if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { - report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "type", - ¶m_ty.to_string(), - ); + report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", param_ty.name); } } ty::GenericParamDefKind::Lifetime => { @@ -140,7 +135,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) tcx, tcx.def_span(param.def_id), "lifetime", - ¶m.name.to_string(), + param.name, ); } } @@ -151,7 +146,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) tcx, tcx.def_span(param.def_id), "const", - ¶m_ct.to_string(), + param_ct.name, ); } } @@ -178,7 +173,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) // used elsewhere are not projected back out. } -fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: &str) { +fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol) { let mut err = struct_span_err!( tcx.sess, span, diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 3084c70589f..0aa085fc71b 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, - ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string", + ExprKind::MethodCall(path, ..) => path.ident.name == sym::to_string, _ => false, }; let sugg = if format_args.format_string_span.contains(value.span) { diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs index 39f68a8a1b4..694f646c707 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString { if_chain! { // Check if item is a method, called to_string and has a parameter 'self' if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; - if impl_item.ident.name.as_str() == "to_string"; + if impl_item.ident.name == sym::to_string; let decl = &signature.decl; if decl.implicit_self.has_implicit_self(); if decl.inputs.len() == 1; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index b4c6bfb31ed..b3276f1394e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -427,5 +427,5 @@ fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: D /// Returns true if the named method is `ToString::to_string`. fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - method_name.as_str() == "to_string" && is_diag_trait_item(cx, method_def_id, sym::ToString) + method_name == sym::to_string && is_diag_trait_item(cx, method_def_id, sym::ToString) }