From c846a2af8ddaa14ff2c2da25bc97bbd8d4284df2 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 14 Jun 2022 10:44:14 -0700 Subject: [PATCH 01/15] Make `std::os::fd` public. `std::os::fd` defines types like `OwnedFd` and `RawFd` and is common between Unix and non-Unix platforms that share a basic file-descriptor concept. Rust currently uses this internally to simplify its own code, but it would be useful for external users in the same way, so make it public. This means that `OwnedFd` etc. will all appear in three places, for example on unix platforms: - `std::os::fd::OwnedFd` - `std::os::unix::io::OwnedFd` - `std::os::unix::prelude::OwnedFd` --- library/std/src/os/fd/mod.rs | 14 ++++++++++++-- library/std/src/os/fd/owned.rs | 5 +---- library/std/src/os/fd/raw.rs | 15 +++------------ library/std/src/os/mod.rs | 2 +- library/std/src/os/unix/io/fd.rs | 8 -------- library/std/src/os/unix/io/mod.rs | 11 +++++------ library/std/src/os/unix/io/raw.rs | 6 ------ library/std/src/os/unix/io/{fd => }/tests.rs | 0 library/std/src/os/wasi/io/mod.rs | 8 +------- 9 files changed, 23 insertions(+), 46 deletions(-) delete mode 100644 library/std/src/os/unix/io/fd.rs delete mode 100644 library/std/src/os/unix/io/raw.rs rename library/std/src/os/unix/io/{fd => }/tests.rs (100%) diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index a456947534a45..8043d1a53d279 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -1,16 +1,26 @@ //! Owned and borrowed Unix-like file descriptors. +//! +//! This module is supported on Unix platforms, and also some non-Unix +//! platforms which use a similar file descriptor system for referencing OS +//! resources. #![stable(feature = "io_safety", since = "1.63.0")] #![deny(unsafe_op_in_unsafe_fn)] // `RawFd`, `AsRawFd`, etc. -pub mod raw; +mod raw; // `OwnedFd`, `AsFd`, etc. -pub mod owned; +mod owned; // Implementations for `AsRawFd` etc. for network types. mod net; #[cfg(test)] mod tests; + +// Export the types and traits for the public API. +#[stable(feature = "io_safety", since = "1.63.0")] +pub use owned::*; +#[stable(feature = "rust1", since = "1.0.0")] +pub use raw::*; diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index a463bc41db7aa..3a1b04a83fa79 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -205,10 +205,7 @@ pub trait AsFd { /// ```rust,no_run /// use std::fs::File; /// # use std::io; - /// # #[cfg(target_os = "wasi")] - /// # use std::os::wasi::io::{AsFd, BorrowedFd}; - /// # #[cfg(unix)] - /// # use std::os::unix::io::{AsFd, BorrowedFd}; + /// # use std::os::fd::{AsFd, BorrowedFd}; /// /// let mut f = File::open("foo.txt")?; /// # #[cfg(any(unix, target_os = "wasi"))] diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 1b3d110426feb..42a9effcf92ab 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -42,10 +42,7 @@ pub trait AsRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; - /// #[cfg(unix)] - /// use std::os::unix::io::{AsRawFd, RawFd}; - /// #[cfg(target_os = "wasi")] - /// use std::os::wasi::io::{AsRawFd, RawFd}; + /// use std::os::fd::{AsRawFd, RawFd}; /// /// let mut f = File::open("foo.txt")?; /// // Note that `raw_fd` is only valid as long as `f` exists. @@ -83,10 +80,7 @@ pub trait FromRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; - /// #[cfg(unix)] - /// use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd}; - /// #[cfg(target_os = "wasi")] - /// use std::os::wasi::io::{FromRawFd, IntoRawFd, RawFd}; + /// use std::os::fd::{FromRawFd, IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; /// # #[cfg(any(unix, target_os = "wasi"))] @@ -121,10 +115,7 @@ pub trait IntoRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; - /// #[cfg(unix)] - /// use std::os::unix::io::{IntoRawFd, RawFd}; - /// #[cfg(target_os = "wasi")] - /// use std::os::wasi::io::{IntoRawFd, RawFd}; + /// use std::os::fd::{IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; /// #[cfg(any(unix, target_os = "wasi"))] diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 18c64b5100764..f62f5af774f0e 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -147,7 +147,7 @@ pub mod solid; pub mod vxworks; #[cfg(any(unix, target_os = "wasi", doc))] -mod fd; +pub mod fd; #[cfg(any(target_os = "linux", target_os = "android", doc))] mod net; diff --git a/library/std/src/os/unix/io/fd.rs b/library/std/src/os/unix/io/fd.rs deleted file mode 100644 index d4cb696459b7e..0000000000000 --- a/library/std/src/os/unix/io/fd.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Owned and borrowed file descriptors. - -// Tests for this module -#[cfg(test)] -mod tests; - -#[stable(feature = "io_safety", since = "1.63.0")] -pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 3ab5606f8897d..25b5dbff14f30 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -77,10 +77,9 @@ #![stable(feature = "rust1", since = "1.0.0")] -mod fd; -mod raw; - -#[stable(feature = "io_safety", since = "1.63.0")] -pub use fd::*; #[stable(feature = "rust1", since = "1.0.0")] -pub use raw::*; +pub use crate::os::fd::*; + +// Tests for this module +#[cfg(test)] +mod tests; diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs deleted file mode 100644 index a4d2ba797d9c4..0000000000000 --- a/library/std/src/os/unix/io/raw.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! Unix-specific extensions to general I/O primitives. - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/unix/io/fd/tests.rs b/library/std/src/os/unix/io/tests.rs similarity index 100% rename from library/std/src/os/unix/io/fd/tests.rs rename to library/std/src/os/unix/io/tests.rs diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs index 6c884e2eaf471..d528590d75b9a 100644 --- a/library/std/src/os/wasi/io/mod.rs +++ b/library/std/src/os/wasi/io/mod.rs @@ -1,12 +1,6 @@ //! WASI-specific extensions to general I/O primitives. -#![deny(unsafe_op_in_unsafe_fn)] #![unstable(feature = "wasi_ext", issue = "71213")] -mod fd; -mod raw; - -#[unstable(feature = "wasi_ext", issue = "71213")] -pub use fd::*; #[unstable(feature = "wasi_ext", issue = "71213")] -pub use raw::*; +pub use crate::os::fd::*; From 09bbc4224f36e05245e63d1766364ea821c83cd3 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 21 Jun 2022 21:09:20 -0700 Subject: [PATCH 02/15] Update asrawfd.js. --- src/test/rustdoc-js-std/asrawfd.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/rustdoc-js-std/asrawfd.js b/src/test/rustdoc-js-std/asrawfd.js index fd228a59099e9..369a34f9c6eb7 100644 --- a/src/test/rustdoc-js-std/asrawfd.js +++ b/src/test/rustdoc-js-std/asrawfd.js @@ -6,9 +6,9 @@ const EXPECTED = { 'others': [ // Reproduction test for https://github.com/rust-lang/rust/issues/78724 // Validate that type alias methods get the correct path. - { 'path': 'std::os::unix::io::AsRawFd', 'name': 'as_raw_fd' }, - { 'path': 'std::os::wasi::io::AsRawFd', 'name': 'as_raw_fd' }, + { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' }, + { 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' }, { 'path': 'std::os::linux::process::PidFd', 'name': 'as_raw_fd' }, - { 'path': 'std::os::unix::io::RawFd', 'name': 'as_raw_fd' }, + { 'path': 'std::os::fd::RawFd', 'name': 'as_raw_fd' }, ], }; From bda12629c3d0926c14b9fbe3f814972f8d50043c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 29 Jun 2022 19:07:43 -0700 Subject: [PATCH 03/15] Clarify that the `fd` module is supported on Unix and WASI. --- library/std/src/os/fd/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index 8043d1a53d279..91dbe44878b1e 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -1,8 +1,7 @@ //! Owned and borrowed Unix-like file descriptors. //! -//! This module is supported on Unix platforms, and also some non-Unix -//! platforms which use a similar file descriptor system for referencing OS -//! resources. +//! This module is supported on Unix platforms and WASI, which both use a +//! similar file descriptor system for referencing OS resources. #![stable(feature = "io_safety", since = "1.63.0")] #![deny(unsafe_op_in_unsafe_fn)] From 7d80510c16bd2172427aff61e5d929aa695cdfa5 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 23 Aug 2022 11:57:13 -0700 Subject: [PATCH 04/15] Re-introduce `unstable` attributes. Add `#[unstable(feature = "os_fd", issue = "98699")]` to the new `pub use` declarations. --- library/std/src/os/fd/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs index 91dbe44878b1e..c6aa7c77dbc41 100644 --- a/library/std/src/os/fd/mod.rs +++ b/library/std/src/os/fd/mod.rs @@ -19,7 +19,7 @@ mod net; mod tests; // Export the types and traits for the public API. -#[stable(feature = "io_safety", since = "1.63.0")] +#[unstable(feature = "os_fd", issue = "98699")] pub use owned::*; -#[stable(feature = "rust1", since = "1.0.0")] +#[unstable(feature = "os_fd", issue = "98699")] pub use raw::*; From a7f3ba9c135fff78728eb7790aae7907a7621922 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 2 Sep 2022 15:35:41 -0700 Subject: [PATCH 05/15] Fix compilation of the doc tests on Windows. --- library/std/src/os/fd/owned.rs | 1 + library/std/src/os/fd/raw.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 3a1b04a83fa79..37250e6a0ae44 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -205,6 +205,7 @@ pub trait AsFd { /// ```rust,no_run /// use std::fs::File; /// # use std::io; + /// # #[cfg(any(unix, target_os = "wasi"))] /// # use std::os::fd::{AsFd, BorrowedFd}; /// /// let mut f = File::open("foo.txt")?; diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index 42a9effcf92ab..f92a05066706d 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -42,6 +42,7 @@ pub trait AsRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; + /// #[cfg(any(unix, target_os = "wasi"))] /// use std::os::fd::{AsRawFd, RawFd}; /// /// let mut f = File::open("foo.txt")?; @@ -80,6 +81,7 @@ pub trait FromRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; + /// #[cfg(any(unix, target_os = "wasi"))] /// use std::os::fd::{FromRawFd, IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; @@ -115,6 +117,7 @@ pub trait IntoRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; + /// #[cfg(any(unix, target_os = "wasi"))] /// use std::os::fd::{IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; From f9ef7e2835b757129b9171c54a6ce948a4a210be Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 21 Sep 2022 01:40:31 +0800 Subject: [PATCH 06/15] code refactoring smart_resolve_report_errors --- .../rustc_resolve/src/late/diagnostics.rs | 434 +++++++++++------- 1 file changed, 261 insertions(+), 173 deletions(-) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index c3c9b0b561782..ac1153602572f 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -130,6 +130,15 @@ pub(super) enum LifetimeElisionCandidate { Missing(MissingLifetime), } +struct BaseError<'a> { + msg: String, + fallback_label: String, + span: Span, + span_label: Option<(Span, &'a str)>, + could_be_expr: bool, + suggestion: Option<(Span, &'a str, String)>, +} + impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn def_span(&self, def_id: DefId) -> Option { match def_id.krate { @@ -138,35 +147,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - /// Handles error reporting for `smart_resolve_path_fragment` function. - /// Creates base error and amends it with one short label and possibly some longer helps/notes. - pub(crate) fn smart_resolve_report_errors( + fn make_base_error( &mut self, path: &[Segment], span: Span, source: PathSource<'_>, res: Option, - ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec) { - let ident_span = path.last().map_or(span, |ident| ident.ident.span); - let ns = source.namespace(); - let is_expected = &|res| source.is_expected(res); - let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); - - debug!(?res, ?source); - + ) -> BaseError<'static> { // Make the base error. - struct BaseError<'a> { - msg: String, - fallback_label: String, - span: Span, - span_label: Option<(Span, &'a str)>, - could_be_expr: bool, - suggestion: Option<(Span, &'a str, String)>, - } let mut expected = source.descr_expected(); let path_str = Segment::names_to_string(path); let item_str = path.last().unwrap().ident; - let base_error = if let Some(res) = res { + if let Some(res) = res { BaseError { msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str), fallback_label: format!("not a {expected}"), @@ -277,53 +269,92 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { could_be_expr: false, suggestion, } - }; + } + } + /// Handles error reporting for `smart_resolve_path_fragment` function. + /// Creates base error and amends it with one short label and possibly some longer helps/notes. + pub(crate) fn smart_resolve_report_errors( + &mut self, + path: &[Segment], + span: Span, + source: PathSource<'_>, + res: Option, + ) -> (DiagnosticBuilder<'a, ErrorGuaranteed>, Vec) { + debug!(?res, ?source); + let base_error = self.make_base_error(path, span, source, res); let code = source.error_code(res.is_some()); let mut err = self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code); self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span); - if let Some((span, label)) = base_error.span_label { + if let Some((span, label)) = base_error.span_label.clone() { err.span_label(span, label); } - if let Some(sugg) = base_error.suggestion { - err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect); + if let Some(ref sugg) = base_error.suggestion { + err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect); } - if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { - err.multipart_suggestion( - "you might have meant to write a `struct` literal", - vec![ - (span.shrink_to_lo(), "{ SomeStruct ".to_string()), - (span.shrink_to_hi(), "}".to_string()), - ], - Applicability::HasPlaceholders, - ); + self.suggest_bare_struct_literal(&mut err); + self.suggest_pattern_match_with_let(&mut err, source, span); + + self.suggest_self_or_self_ref(&mut err, path, span); + self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error); + if self.suggest_self_ty_or_self_value(&mut err, source, path, span) { + return (err, Vec::new()); } - match (source, self.diagnostic_metadata.in_if_condition) { - ( - PathSource::Expr(_), - Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }), - ) => { - // Icky heuristic so we don't suggest: - // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern) - // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span) - if lhs.is_approximately_pattern() && lhs.span.contains(span) { - err.span_suggestion_verbose( - expr_span.shrink_to_lo(), - "you might have meant to use pattern matching", - "let ", - Applicability::MaybeIncorrect, - ); + + let (found, candidates) = + self.try_lookup_name_with_relex_fashion(&mut err, source, path, span, res, &base_error); + if found { + return (err, candidates); + } + + self.suggest_type_ascription(&mut err, source, path, res, span, &base_error); + self.add_err_code_cases(&mut err, source, path, span); + + (err, candidates) + } + + fn detect_assoct_type_constraint_meant_as_path( + &self, + err: &mut Diagnostic, + base_error: &BaseError<'static>, + ) { + let Some(ty) = self.diagnostic_metadata.current_type_path else { return; }; + let TyKind::Path(_, path) = &ty.kind else { return; }; + for segment in &path.segments { + let Some(params) = &segment.args else { continue; }; + let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; }; + for param in ¶ms.args { + let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; }; + let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else { + continue; + }; + for bound in bounds { + let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None) + = bound else + { + continue; + }; + if base_error.span == trait_ref.span { + err.span_suggestion_verbose( + constraint.ident.span.between(trait_ref.span), + "you might have meant to write a path instead of an associated type bound", + "::", + Applicability::MachineApplicable, + ); + } } } - _ => {} } + } + fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) { let is_assoc_fn = self.self_type_is_available(); + let item_str = path.last().unwrap().ident; // Emit help message for fake-self from other languages (e.g., `this` in Javascript). if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn { err.span_suggestion_short( @@ -358,96 +389,25 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } } + } - self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err); - - // Emit special messages for unresolved `Self` and `self`. - if is_self_type(path, ns) { - err.code(rustc_errors::error_code!(E0411)); - err.span_label( - span, - "`Self` is only available in impls, traits, and type definitions".to_string(), - ); - if let Some(item_kind) = self.diagnostic_metadata.current_item { - err.span_label( - item_kind.ident.span, - format!( - "`Self` not allowed in {} {}", - item_kind.kind.article(), - item_kind.kind.descr() - ), - ); - } - return (err, Vec::new()); - } - if is_self_value(path, ns) { - debug!("smart_resolve_path_fragment: E0424, source={:?}", source); - - err.code(rustc_errors::error_code!(E0424)); - err.span_label(span, match source { - PathSource::Pat => "`self` value is a keyword and may not be bound to variables or shadowed", - _ => "`self` value is a keyword only available in methods with a `self` parameter", - }); - if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { - // The current function has a `self' parameter, but we were unable to resolve - // a reference to `self`. This can only happen if the `self` identifier we - // are resolving came from a different hygiene context. - if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { - err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); - } else { - let doesnt = if is_assoc_fn { - let (span, sugg) = fn_kind - .decl() - .inputs - .get(0) - .map(|p| (p.span.shrink_to_lo(), "&self, ")) - .unwrap_or_else(|| { - // Try to look for the "(" after the function name, if possible. - // This avoids placing the suggestion into the visibility specifier. - let span = fn_kind - .ident() - .map_or(*span, |ident| span.with_lo(ident.span.hi())); - ( - self.r - .session - .source_map() - .span_through_char(span, '(') - .shrink_to_hi(), - "&self", - ) - }); - err.span_suggestion_verbose( - span, - "add a `self` receiver parameter to make the associated `fn` a method", - sugg, - Applicability::MaybeIncorrect, - ); - "doesn't" - } else { - "can't" - }; - if let Some(ident) = fn_kind.ident() { - err.span_label( - ident.span, - &format!("this function {} have a `self` parameter", doesnt), - ); - } - } - } else if let Some(item_kind) = self.diagnostic_metadata.current_item { - err.span_label( - item_kind.ident.span, - format!( - "`self` not allowed in {} {}", - item_kind.kind.article(), - item_kind.kind.descr() - ), - ); - } - return (err, Vec::new()); - } - + fn try_lookup_name_with_relex_fashion( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + span: Span, + res: Option, + base_error: &BaseError<'static>, + ) -> (bool, Vec) { // Try to lookup name in more relaxed fashion for better error reporting. let ident = path.last().unwrap().ident; + let is_expected = &|res| source.is_expected(res); + let ns = source.namespace(); + let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); + let path_str = Segment::names_to_string(path); + let ident_span = path.last().map_or(span, |ident| ident.ident.span); + let mut candidates = self .r .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected) @@ -494,7 +454,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { { // Already reported this issue on the lhs of the type ascription. err.delay_as_bug(); - return (err, candidates); + return (true, candidates); } } @@ -522,8 +482,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ); } } + // Try Levenshtein algorithm. - let typo_sugg = self.lookup_typo_candidate(path, ns, is_expected); + let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); if path.len() == 1 && self.self_type_is_available() { if let Some(candidate) = self.lookup_assoc_candidate(ident, ns, is_expected) { let self_is_available = self.self_value_is_available(path[0].ident.span); @@ -560,8 +521,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ); } } - self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span); - return (err, candidates); + self.r.add_typo_suggestion(err, typo_sugg, ident_span); + return (true, candidates); } // If the first argument in call is `self` suggest calling a method. @@ -579,14 +540,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { format!("self.{path_str}({args_snippet})"), Applicability::MachineApplicable, ); - return (err, candidates); + return (true, candidates); } } // Try context-dependent help if relaxed lookup didn't work. if let Some(res) = res { if self.smart_resolve_context_dependent_help( - &mut err, + err, span, source, res, @@ -594,14 +555,25 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { &base_error.fallback_label, ) { // We do this to avoid losing a secondary span when we override the main error span. - self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span); - return (err, candidates); + self.r.add_typo_suggestion(err, typo_sugg, ident_span); + return (true, candidates); } } + return (false, candidates); + } + fn suggest_type_ascription( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + res: Option, + span: Span, + base_error: &BaseError<'static>, + ) { let is_macro = base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none(); - if !self.type_ascription_suggestion(&mut err, base_error.span) { + if !self.type_ascription_suggestion(err, base_error.span) { let mut fallback = false; if let ( PathSource::Trait(AliasPossibility::Maybe), @@ -663,9 +635,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err); + let is_expected = &|res| source.is_expected(res); + let ident_span = path.last().map_or(span, |ident| ident.ident.span); + fallback |= self.restrict_assoc_type_in_where_clause(span, err); - if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) { + let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); + if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) { fallback = true; match self.diagnostic_metadata.current_let_binding { Some((pat_sp, Some(ty_sp), None)) @@ -683,17 +658,27 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // If the trait has a single item (which wasn't matched by Levenshtein), suggest it let suggestion = self.get_single_associated_item(&path, &source, is_expected); - self.r.add_typo_suggestion(&mut err, suggestion, ident_span); + self.r.add_typo_suggestion(err, suggestion, ident_span); } if fallback { // Fallback label. - err.span_label(base_error.span, base_error.fallback_label); + err.span_label(base_error.span, &base_error.fallback_label); } } + } + + fn add_err_code_cases( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + span: Span, + ) { if let Some(err_code) = &err.code { if err_code == &rustc_errors::error_code!(E0425) { for label_rib in &self.label_ribs { for (label_ident, node_id) in &label_rib.bindings { + let ident = path.last().unwrap().ident; if format!("'{}", ident) == label_ident.to_string() { err.span_label(label_ident.span, "a label with a similar name exists"); if let PathSource::Expr(Some(Expr { @@ -724,38 +709,106 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } } - - (err, candidates) } - fn detect_assoct_type_constraint_meant_as_path(&self, base_span: Span, err: &mut Diagnostic) { - let Some(ty) = self.diagnostic_metadata.current_type_path else { return; }; - let TyKind::Path(_, path) = &ty.kind else { return; }; - for segment in &path.segments { - let Some(params) = &segment.args else { continue; }; - let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else { continue; }; - for param in ¶ms.args { - let ast::AngleBracketedArg::Constraint(constraint) = param else { continue; }; - let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else { - continue; - }; - for bound in bounds { - let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifier::None) - = bound else - { - continue; - }; - if base_span == trait_ref.span { + /// Emit special messages for unresolved `Self` and `self`. + fn suggest_self_ty_or_self_value( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + path: &[Segment], + span: Span, + ) -> bool { + if is_self_type(path, source.namespace()) { + err.code(rustc_errors::error_code!(E0411)); + err.span_label( + span, + "`Self` is only available in impls, traits, and type definitions".to_string(), + ); + if let Some(item_kind) = self.diagnostic_metadata.current_item { + err.span_label( + item_kind.ident.span, + format!( + "`Self` not allowed in {} {}", + item_kind.kind.article(), + item_kind.kind.descr() + ), + ); + } + return true; + } + + if is_self_value(path, source.namespace()) { + debug!("smart_resolve_path_fragment: E0424, source={:?}", source); + err.code(rustc_errors::error_code!(E0424)); + err.span_label( + span, + match source { + PathSource::Pat => { + "`self` value is a keyword and may not be bound to variables or shadowed" + } + _ => "`self` value is a keyword only available in methods with a `self` parameter", + }, + ); + let is_assoc_fn = self.self_type_is_available(); + if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { + // The current function has a `self' parameter, but we were unable to resolve + // a reference to `self`. This can only happen if the `self` identifier we + // are resolving came from a different hygiene context. + if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { + err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); + } else { + let doesnt = if is_assoc_fn { + let (span, sugg) = fn_kind + .decl() + .inputs + .get(0) + .map(|p| (p.span.shrink_to_lo(), "&self, ")) + .unwrap_or_else(|| { + // Try to look for the "(" after the function name, if possible. + // This avoids placing the suggestion into the visibility specifier. + let span = fn_kind + .ident() + .map_or(*span, |ident| span.with_lo(ident.span.hi())); + ( + self.r + .session + .source_map() + .span_through_char(span, '(') + .shrink_to_hi(), + "&self", + ) + }); err.span_suggestion_verbose( - constraint.ident.span.between(trait_ref.span), - "you might have meant to write a path instead of an associated type bound", - "::", - Applicability::MachineApplicable, + span, + "add a `self` receiver parameter to make the associated `fn` a method", + sugg, + Applicability::MaybeIncorrect, + ); + "doesn't" + } else { + "can't" + }; + if let Some(ident) = fn_kind.ident() { + err.span_label( + ident.span, + &format!("this function {} have a `self` parameter", doesnt), ); } } + } else if let Some(item_kind) = self.diagnostic_metadata.current_item { + err.span_label( + item_kind.ident.span, + format!( + "`self` not allowed in {} {}", + item_kind.kind.article(), + item_kind.kind.descr() + ), + ); } + return true; } + false } fn suggest_swapping_misplaced_self_ty_and_trait( @@ -787,6 +840,41 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } + fn suggest_bare_struct_literal(&mut self, err: &mut Diagnostic) { + if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { + err.multipart_suggestion( + "you might have meant to write a `struct` literal", + vec![ + (span.shrink_to_lo(), "{ SomeStruct ".to_string()), + (span.shrink_to_hi(), "}".to_string()), + ], + Applicability::HasPlaceholders, + ); + } + } + + fn suggest_pattern_match_with_let( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + span: Span, + ) { + if let PathSource::Expr(_) = source && + let Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. } ) = self.diagnostic_metadata.in_if_condition { + // Icky heuristic so we don't suggest: + // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern) + // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span) + if lhs.is_approximately_pattern() && lhs.span.contains(span) { + err.span_suggestion_verbose( + expr_span.shrink_to_lo(), + "you might have meant to use pattern matching", + "let ", + Applicability::MaybeIncorrect, + ); + } + } + } + fn get_single_associated_item( &mut self, path: &[Segment], From 7adfb44b95335ce9a565aea1f5f32a3e14119f9c Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 21 Sep 2022 12:31:29 +0800 Subject: [PATCH 07/15] add trivial comments --- compiler/rustc_resolve/src/late/diagnostics.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index ac1153602572f..7f3dc2085211f 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -130,6 +130,7 @@ pub(super) enum LifetimeElisionCandidate { Missing(MissingLifetime), } +/// Only used for diagnostics. struct BaseError<'a> { msg: String, fallback_label: String, @@ -313,7 +314,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } self.suggest_type_ascription(&mut err, source, path, res, span, &base_error); - self.add_err_code_cases(&mut err, source, path, span); + self.err_code_special_cases(&mut err, source, path, span); (err, candidates) } @@ -667,7 +668,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - fn add_err_code_cases( + fn err_code_special_cases( &mut self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, source: PathSource<'_>, From fdda7e0a33a1aec94966248de4a210d45ead67a6 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 26 Sep 2022 00:40:57 +0800 Subject: [PATCH 08/15] more code refactor on smart_resolve_report_errors --- .../rustc_resolve/src/late/diagnostics.rs | 382 ++++++++++-------- 1 file changed, 207 insertions(+), 175 deletions(-) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 7f3dc2085211f..c82e59ca44f12 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -131,13 +131,13 @@ pub(super) enum LifetimeElisionCandidate { } /// Only used for diagnostics. -struct BaseError<'a> { +struct BaseError { msg: String, fallback_label: String, span: Span, - span_label: Option<(Span, &'a str)>, + span_label: Option<(Span, &'static str)>, could_be_expr: bool, - suggestion: Option<(Span, &'a str, String)>, + suggestion: Option<(Span, &'static str, String)>, } impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { @@ -154,7 +154,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { span: Span, source: PathSource<'_>, res: Option, - ) -> BaseError<'static> { + ) -> BaseError { // Make the base error. let mut expected = source.descr_expected(); let path_str = Segment::names_to_string(path); @@ -290,7 +290,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span); - if let Some((span, label)) = base_error.span_label.clone() { + if let Some((span, label)) = base_error.span_label { err.span_label(span, label); } @@ -303,17 +303,29 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { self.suggest_self_or_self_ref(&mut err, path, span); self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error); - if self.suggest_self_ty_or_self_value(&mut err, source, path, span) { + if self.suggest_self_ty(&mut err, source, path, span) + || self.suggest_self_value(&mut err, source, path, span) + { return (err, Vec::new()); } let (found, candidates) = - self.try_lookup_name_with_relex_fashion(&mut err, source, path, span, res, &base_error); + self.try_lookup_name_relaxed(&mut err, source, path, span, res, &base_error); if found { return (err, candidates); } - self.suggest_type_ascription(&mut err, source, path, res, span, &base_error); + if !self.type_ascription_suggestion(&mut err, base_error.span) { + let mut fallback = + self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error); + if self.suggest_typo(&mut err, source, path, span, &base_error) { + fallback = true; + } + if fallback { + // Fallback label. + err.span_label(base_error.span, &base_error.fallback_label); + } + } self.err_code_special_cases(&mut err, source, path, span); (err, candidates) @@ -322,7 +334,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn detect_assoct_type_constraint_meant_as_path( &self, err: &mut Diagnostic, - base_error: &BaseError<'static>, + base_error: &BaseError, ) { let Some(ty) = self.diagnostic_metadata.current_type_path else { return; }; let TyKind::Path(_, path) = &ty.kind else { return; }; @@ -355,7 +367,8 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) { let is_assoc_fn = self.self_type_is_available(); - let item_str = path.last().unwrap().ident; + let Some(path_last_segment) = path.last() else { return }; + let item_str = path_last_segment.ident; // Emit help message for fake-self from other languages (e.g., `this` in Javascript). if ["this", "my"].contains(&item_str.as_str()) && is_assoc_fn { err.span_suggestion_short( @@ -392,14 +405,14 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - fn try_lookup_name_with_relex_fashion( + fn try_lookup_name_relaxed( &mut self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, source: PathSource<'_>, path: &[Segment], span: Span, res: Option, - base_error: &BaseError<'static>, + base_error: &BaseError, ) -> (bool, Vec) { // Try to lookup name in more relaxed fashion for better error reporting. let ident = path.last().unwrap().ident; @@ -563,109 +576,114 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { return (false, candidates); } - fn suggest_type_ascription( + fn suggest_trait_and_bounds( &mut self, err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, source: PathSource<'_>, - path: &[Segment], res: Option, span: Span, - base_error: &BaseError<'static>, - ) { + base_error: &BaseError, + ) -> bool { let is_macro = base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none(); - if !self.type_ascription_suggestion(err, base_error.span) { - let mut fallback = false; - if let ( - PathSource::Trait(AliasPossibility::Maybe), - Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), - false, - ) = (source, res, is_macro) - { - if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { - fallback = true; - let spans: Vec = bounds - .iter() - .map(|bound| bound.span()) - .filter(|&sp| sp != base_error.span) - .collect(); + let mut fallback = false; - let start_span = bounds[0].span(); - // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) - let end_span = bounds.last().unwrap().span(); - // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) - let last_bound_span = spans.last().cloned().unwrap(); - let mut multi_span: MultiSpan = spans.clone().into(); - for sp in spans { - let msg = if sp == last_bound_span { - format!( - "...because of {these} bound{s}", - these = pluralize!("this", bounds.len() - 1), - s = pluralize!(bounds.len() - 1), - ) - } else { - String::new() - }; - multi_span.push_span_label(sp, msg); - } - multi_span - .push_span_label(base_error.span, "expected this type to be a trait..."); - err.span_help( - multi_span, - "`+` is used to constrain a \"trait object\" type with lifetimes or \ - auto-traits; structs and enums can't be bound in that way", - ); - if bounds.iter().all(|bound| match bound { - ast::GenericBound::Outlives(_) => true, - ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, - }) { - let mut sugg = vec![]; - if base_error.span != start_span { - sugg.push((start_span.until(base_error.span), String::new())); - } - if base_error.span != end_span { - sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); - } + if let ( + PathSource::Trait(AliasPossibility::Maybe), + Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), + false, + ) = (source, res, is_macro) + { + if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { + fallback = true; + let spans: Vec = bounds + .iter() + .map(|bound| bound.span()) + .filter(|&sp| sp != base_error.span) + .collect(); - err.multipart_suggestion( - "if you meant to use a type and not a trait here, remove the bounds", - sugg, - Applicability::MaybeIncorrect, - ); + let start_span = bounds[0].span(); + // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) + let end_span = bounds.last().unwrap().span(); + // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) + let last_bound_span = spans.last().cloned().unwrap(); + let mut multi_span: MultiSpan = spans.clone().into(); + for sp in spans { + let msg = if sp == last_bound_span { + format!( + "...because of {these} bound{s}", + these = pluralize!("this", bounds.len() - 1), + s = pluralize!(bounds.len() - 1), + ) + } else { + String::new() + }; + multi_span.push_span_label(sp, msg); + } + multi_span.push_span_label(base_error.span, "expected this type to be a trait..."); + err.span_help( + multi_span, + "`+` is used to constrain a \"trait object\" type with lifetimes or \ + auto-traits; structs and enums can't be bound in that way", + ); + if bounds.iter().all(|bound| match bound { + ast::GenericBound::Outlives(_) => true, + ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, + }) { + let mut sugg = vec![]; + if base_error.span != start_span { + sugg.push((start_span.until(base_error.span), String::new())); + } + if base_error.span != end_span { + sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); } + + err.multipart_suggestion( + "if you meant to use a type and not a trait here, remove the bounds", + sugg, + Applicability::MaybeIncorrect, + ); } } + } - let is_expected = &|res| source.is_expected(res); - let ident_span = path.last().map_or(span, |ident| ident.ident.span); - fallback |= self.restrict_assoc_type_in_where_clause(span, err); + fallback |= self.restrict_assoc_type_in_where_clause(span, err); + fallback + } - let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); - if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) { - fallback = true; - match self.diagnostic_metadata.current_let_binding { - Some((pat_sp, Some(ty_sp), None)) - if ty_sp.contains(base_error.span) && base_error.could_be_expr => - { - err.span_suggestion_short( - pat_sp.between(ty_sp), - "use `=` if you meant to assign", - " = ", - Applicability::MaybeIncorrect, - ); - } - _ => {} + fn suggest_typo( + &mut self, + err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, + source: PathSource<'_>, + path: &[Segment], + span: Span, + base_error: &BaseError, + ) -> bool { + let is_expected = &|res| source.is_expected(res); + let ident_span = path.last().map_or(span, |ident| ident.ident.span); + let typo_sugg = self.lookup_typo_candidate(path, source.namespace(), is_expected); + let mut fallback = false; + if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) { + fallback = true; + match self.diagnostic_metadata.current_let_binding { + Some((pat_sp, Some(ty_sp), None)) + if ty_sp.contains(base_error.span) && base_error.could_be_expr => + { + err.span_suggestion_short( + pat_sp.between(ty_sp), + "use `=` if you meant to assign", + " = ", + Applicability::MaybeIncorrect, + ); } - - // If the trait has a single item (which wasn't matched by Levenshtein), suggest it - let suggestion = self.get_single_associated_item(&path, &source, is_expected); - self.r.add_typo_suggestion(err, suggestion, ident_span); - } - if fallback { - // Fallback label. - err.span_label(base_error.span, &base_error.fallback_label); + _ => {} } + + // If the trait has a single item (which wasn't matched by Levenshtein), suggest it + let suggestion = self.get_single_associated_item(&path, &source, is_expected); + self.r.add_typo_suggestion(err, suggestion, ident_span); } + fallback } fn err_code_special_cases( @@ -713,36 +731,48 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } /// Emit special messages for unresolved `Self` and `self`. - fn suggest_self_ty_or_self_value( + fn suggest_self_ty( &mut self, err: &mut Diagnostic, source: PathSource<'_>, path: &[Segment], span: Span, ) -> bool { - if is_self_type(path, source.namespace()) { - err.code(rustc_errors::error_code!(E0411)); + if !is_self_type(path, source.namespace()) { + return false; + } + err.code(rustc_errors::error_code!(E0411)); + err.span_label( + span, + "`Self` is only available in impls, traits, and type definitions".to_string(), + ); + if let Some(item_kind) = self.diagnostic_metadata.current_item { err.span_label( - span, - "`Self` is only available in impls, traits, and type definitions".to_string(), + item_kind.ident.span, + format!( + "`Self` not allowed in {} {}", + item_kind.kind.article(), + item_kind.kind.descr() + ), ); - if let Some(item_kind) = self.diagnostic_metadata.current_item { - err.span_label( - item_kind.ident.span, - format!( - "`Self` not allowed in {} {}", - item_kind.kind.article(), - item_kind.kind.descr() - ), - ); - } - return true; } + true + } - if is_self_value(path, source.namespace()) { - debug!("smart_resolve_path_fragment: E0424, source={:?}", source); - err.code(rustc_errors::error_code!(E0424)); - err.span_label( + fn suggest_self_value( + &mut self, + err: &mut Diagnostic, + source: PathSource<'_>, + path: &[Segment], + span: Span, + ) -> bool { + if !is_self_value(path, source.namespace()) { + return false; + } + + debug!("smart_resolve_path_fragment: E0424, source={:?}", source); + err.code(rustc_errors::error_code!(E0424)); + err.span_label( span, match source { PathSource::Pat => { @@ -750,66 +780,64 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } _ => "`self` value is a keyword only available in methods with a `self` parameter", }, - ); - let is_assoc_fn = self.self_type_is_available(); - if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { - // The current function has a `self' parameter, but we were unable to resolve - // a reference to `self`. This can only happen if the `self` identifier we - // are resolving came from a different hygiene context. - if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { - err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); + ); + let is_assoc_fn = self.self_type_is_available(); + if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function { + // The current function has a `self' parameter, but we were unable to resolve + // a reference to `self`. This can only happen if the `self` identifier we + // are resolving came from a different hygiene context. + if fn_kind.decl().inputs.get(0).map_or(false, |p| p.is_self()) { + err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters"); + } else { + let doesnt = if is_assoc_fn { + let (span, sugg) = fn_kind + .decl() + .inputs + .get(0) + .map(|p| (p.span.shrink_to_lo(), "&self, ")) + .unwrap_or_else(|| { + // Try to look for the "(" after the function name, if possible. + // This avoids placing the suggestion into the visibility specifier. + let span = fn_kind + .ident() + .map_or(*span, |ident| span.with_lo(ident.span.hi())); + ( + self.r + .session + .source_map() + .span_through_char(span, '(') + .shrink_to_hi(), + "&self", + ) + }); + err.span_suggestion_verbose( + span, + "add a `self` receiver parameter to make the associated `fn` a method", + sugg, + Applicability::MaybeIncorrect, + ); + "doesn't" } else { - let doesnt = if is_assoc_fn { - let (span, sugg) = fn_kind - .decl() - .inputs - .get(0) - .map(|p| (p.span.shrink_to_lo(), "&self, ")) - .unwrap_or_else(|| { - // Try to look for the "(" after the function name, if possible. - // This avoids placing the suggestion into the visibility specifier. - let span = fn_kind - .ident() - .map_or(*span, |ident| span.with_lo(ident.span.hi())); - ( - self.r - .session - .source_map() - .span_through_char(span, '(') - .shrink_to_hi(), - "&self", - ) - }); - err.span_suggestion_verbose( - span, - "add a `self` receiver parameter to make the associated `fn` a method", - sugg, - Applicability::MaybeIncorrect, - ); - "doesn't" - } else { - "can't" - }; - if let Some(ident) = fn_kind.ident() { - err.span_label( - ident.span, - &format!("this function {} have a `self` parameter", doesnt), - ); - } + "can't" + }; + if let Some(ident) = fn_kind.ident() { + err.span_label( + ident.span, + &format!("this function {} have a `self` parameter", doesnt), + ); } - } else if let Some(item_kind) = self.diagnostic_metadata.current_item { - err.span_label( - item_kind.ident.span, - format!( - "`self` not allowed in {} {}", - item_kind.kind.article(), - item_kind.kind.descr() - ), - ); } - return true; + } else if let Some(item_kind) = self.diagnostic_metadata.current_item { + err.span_label( + item_kind.ident.span, + format!( + "`self` not allowed in {} {}", + item_kind.kind.article(), + item_kind.kind.descr() + ), + ); } - false + true } fn suggest_swapping_misplaced_self_ty_and_trait( @@ -861,7 +889,11 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { span: Span, ) { if let PathSource::Expr(_) = source && - let Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. } ) = self.diagnostic_metadata.in_if_condition { + let Some(Expr { + span: expr_span, + kind: ExprKind::Assign(lhs, _, _), + .. + }) = self.diagnostic_metadata.in_if_condition { // Icky heuristic so we don't suggest: // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern) // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span) From db0877f653c61f79457f02ea0bb643017cbb2490 Mon Sep 17 00:00:00 2001 From: yukang Date: Mon, 26 Sep 2022 00:53:55 +0800 Subject: [PATCH 09/15] trivial fix on fallback --- compiler/rustc_resolve/src/late/diagnostics.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index c82e59ca44f12..bd3336b715311 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -318,9 +318,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { if !self.type_ascription_suggestion(&mut err, base_error.span) { let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error); - if self.suggest_typo(&mut err, source, path, span, &base_error) { - fallback = true; - } + fallback |= self.suggest_typo(&mut err, source, path, span, &base_error); if fallback { // Fallback label. err.span_label(base_error.span, &base_error.fallback_label); From cca48285b87e44f34788773eef520f9e3f9b20c9 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Sep 2022 23:39:27 +0000 Subject: [PATCH 10/15] Format type_of --- .../rustc_hir_analysis/src/collect/type_of.rs | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 24fb0b1fd26b5..ee7d0e84b57ad 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -333,7 +333,12 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { find_opaque_ty_constraints_for_tait(tcx, def_id) } // Opaque types desugared from `impl Trait`. - ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), in_trait, .. }) => { + ItemKind::OpaqueTy(OpaqueTy { + origin: + hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), + in_trait, + .. + }) => { if in_trait { span_bug!(item.span, "impl-trait in trait has no default") } else { @@ -378,7 +383,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { Node::Field(field) => icx.to_ty(field.ty), - Node::Expr(&Expr { kind: ExprKind::Closure{..}, .. }) => tcx.typeck(def_id).node_type(hir_id), + Node::Expr(&Expr { kind: ExprKind::Closure { .. }, .. }) => { + tcx.typeck(def_id).node_type(hir_id) + } Node::AnonConst(_) if let Some(param) = tcx.opt_const_param_of(def_id) => { // We defer to `type_of` of the corresponding parameter @@ -410,40 +417,42 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { | Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. }) if asm.operands.iter().any(|(op, _op_sp)| match op { hir::InlineAsmOperand::Const { anon_const } - | hir::InlineAsmOperand::SymFn { anon_const } => anon_const.hir_id == hir_id, + | hir::InlineAsmOperand::SymFn { anon_const } => { + anon_const.hir_id == hir_id + } _ => false, }) => { tcx.typeck(def_id).node_type(hir_id) } - Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => tcx - .adt_def(tcx.hir().get_parent_item(hir_id)) - .repr() - .discr_type() - .to_ty(tcx), + Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => { + tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx) + } - Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. }) - if let Node::TraitRef(trait_ref) = tcx.hir().get( - tcx.hir().get_parent_node(binding_id) - ) => + Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. }) + if let Node::TraitRef(trait_ref) = + tcx.hir().get(tcx.hir().get_parent_node(binding_id)) => { - let Some(trait_def_id) = trait_ref.trait_def_id() else { + let Some(trait_def_id) = trait_ref.trait_def_id() else { return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); }; - let assoc_items = tcx.associated_items(trait_def_id); - let assoc_item = assoc_items.find_by_name_and_kind( - tcx, binding.ident, ty::AssocKind::Const, def_id.to_def_id(), - ); - if let Some(assoc_item) = assoc_item { - tcx.type_of(assoc_item.def_id) - } else { - // FIXME(associated_const_equality): add a useful error message here. - tcx.ty_error_with_message( - DUMMY_SP, - "Could not find associated const on trait", - ) - } + let assoc_items = tcx.associated_items(trait_def_id); + let assoc_item = assoc_items.find_by_name_and_kind( + tcx, + binding.ident, + ty::AssocKind::Const, + def_id.to_def_id(), + ); + if let Some(assoc_item) = assoc_item { + tcx.type_of(assoc_item.def_id) + } else { + // FIXME(associated_const_equality): add a useful error message here. + tcx.ty_error_with_message( + DUMMY_SP, + "Could not find associated const on trait", + ) + } } Node::GenericParam(&GenericParam { @@ -452,8 +461,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { .. }) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)), - x => - tcx.ty_error_with_message( + x => tcx.ty_error_with_message( DUMMY_SP, &format!("unexpected const parent in type_of(): {x:?}"), ), From 05267b5a50bfa3952fe2a4433f4e12a93e15dd22 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Sep 2022 23:44:43 +0000 Subject: [PATCH 11/15] Make type_of work correctly for const arg bindings --- compiler/rustc_hir_analysis/src/collect/type_of.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index ee7d0e84b57ad..227d633a1e44c 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -430,9 +430,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx) } - Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. }) - if let Node::TraitRef(trait_ref) = - tcx.hir().get(tcx.hir().get_parent_node(binding_id)) => + Node::TypeBinding( + binding @ &TypeBinding { + hir_id: binding_id, + kind: TypeBindingKind::Equality { term: Term::Const(ref e) }, + .. + }, + ) if let Node::TraitRef(trait_ref) = + tcx.hir().get(tcx.hir().get_parent_node(binding_id)) + && e.hir_id == hir_id => { let Some(trait_def_id) = trait_ref.trait_def_id() else { return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); From 92561f43f185b54c81167ddc89fbe2dcd94b4efb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 26 Sep 2022 23:57:33 +0000 Subject: [PATCH 12/15] Support bindings with anon consts in generics --- .../rustc_hir_analysis/src/collect/type_of.rs | 47 ++++++++++++++++++- .../generic-associated-types/issue-102333.rs | 15 ++++++ 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/generic-associated-types/issue-102333.rs diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 227d633a1e44c..f8a62c8491076 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -441,8 +441,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { && e.hir_id == hir_id => { let Some(trait_def_id) = trait_ref.trait_def_id() else { - return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); - }; + return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); + }; let assoc_items = tcx.associated_items(trait_def_id); let assoc_item = assoc_items.find_by_name_and_kind( tcx, @@ -461,6 +461,49 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } } + Node::TypeBinding( + binding @ &TypeBinding { hir_id: binding_id, gen_args, ref kind, .. }, + ) if let Node::TraitRef(trait_ref) = + tcx.hir().get(tcx.hir().get_parent_node(binding_id)) + && let Some((idx, _)) = + gen_args.args.iter().enumerate().find(|(_, arg)| { + if let GenericArg::Const(ct) = arg { + ct.value.hir_id == hir_id + } else { + false + } + }) => + { + let Some(trait_def_id) = trait_ref.trait_def_id() else { + return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"); + }; + let assoc_items = tcx.associated_items(trait_def_id); + let assoc_item = assoc_items.find_by_name_and_kind( + tcx, + binding.ident, + match kind { + // I think `` type bindings requires that `A` is a type + TypeBindingKind::Constraint { .. } + | TypeBindingKind::Equality { term: Term::Ty(..) } => { + ty::AssocKind::Type + } + TypeBindingKind::Equality { term: Term::Const(..) } => { + ty::AssocKind::Const + } + }, + def_id.to_def_id(), + ); + if let Some(assoc_item) = assoc_item { + tcx.type_of(tcx.generics_of(assoc_item.def_id).params[idx].def_id) + } else { + // FIXME(associated_const_equality): add a useful error message here. + tcx.ty_error_with_message( + DUMMY_SP, + "Could not find associated const on trait", + ) + } + } + Node::GenericParam(&GenericParam { hir_id: param_hir_id, kind: GenericParamKind::Const { default: Some(ct), .. }, diff --git a/src/test/ui/generic-associated-types/issue-102333.rs b/src/test/ui/generic-associated-types/issue-102333.rs new file mode 100644 index 0000000000000..6c72563322f55 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-102333.rs @@ -0,0 +1,15 @@ +// check-pass + +trait A { + type T: B = ()>; +} + +trait B { + type U; +} + +fn f() { + let _: <::T as B>::U<1i32> = (); +} + +fn main() {} From 7f06d513fbbb01af78fae3586114abd9077b930e Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 27 Sep 2022 17:33:59 -0700 Subject: [PATCH 13/15] Don't export `__heap_base` and `__data_end` on wasm32-wasi. `__heap_base` and `__data_end` are exported for use by wasm-bindgen, which uses the wasm32-unknown-unknown target. On wasm32-wasi, as a step toward implementing the Canonical ABI, and as an aid to building speicalized WASI API polyfill wrappers, don't export `__heap_base` and `__data_end` on wasm32-wasi. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e0bd7a33f7373..061053ef2ac97 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1320,10 +1320,12 @@ impl<'a> Linker for WasmLd<'a> { // LLD will hide these otherwise-internal symbols since it only exports // symbols explicitly passed via the `--export` flags above and hides all - // others. Various bits and pieces of tooling use this, so be sure these - // symbols make their way out of the linker as well. - self.cmd.arg("--export=__heap_base"); - self.cmd.arg("--export=__data_end"); + // others. Various bits and pieces of wasm32-unknown-unknown tooling use + // this, so be sure these symbols make their way out of the linker as well. + if self.sess.target.os == "unknown" { + self.cmd.arg("--export=__heap_base"); + self.cmd.arg("--export=__data_end"); + } } fn subsystem(&mut self, _subsystem: &str) {} From 356a52cca8747aeaf8e60e5521594e81b1ff69d1 Mon Sep 17 00:00:00 2001 From: Rageking8 Date: Wed, 28 Sep 2022 16:30:06 +0800 Subject: [PATCH 14/15] add regression test --- src/test/ui/generics/issue-94923.rs | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/test/ui/generics/issue-94923.rs diff --git a/src/test/ui/generics/issue-94923.rs b/src/test/ui/generics/issue-94923.rs new file mode 100644 index 0000000000000..d337a5dffc934 --- /dev/null +++ b/src/test/ui/generics/issue-94923.rs @@ -0,0 +1,49 @@ +// run-pass +// regression test for issue #94923 +// min-llvm-version: 15.0.0 +// compile-flags: -C opt-level=3 + +fn f0(mut x: usize) -> usize { + for _ in 0..1000 { + x *= 123; + x %= 99 + } + x + 321 // function composition is not just longer iteration +} + +fn f1(x: usize) -> usize { + f0::<(i8, T)>(f0::<(u8, T)>(x)) +} + +fn f2(x: usize) -> usize { + f1::<(i8, T)>(f1::<(u8, T)>(x)) +} + +fn f3(x: usize) -> usize { + f2::<(i8, T)>(f2::<(u8, T)>(x)) +} + +fn f4(x: usize) -> usize { + f3::<(i8, T)>(f3::<(u8, T)>(x)) +} + +fn f5(x: usize) -> usize { + f4::<(i8, T)>(f4::<(u8, T)>(x)) +} + +fn f6(x: usize) -> usize { + f5::<(i8, T)>(f5::<(u8, T)>(x)) +} + +fn f7(x: usize) -> usize { + f6::<(i8, T)>(f6::<(u8, T)>(x)) +} + +fn f8(x: usize) -> usize { + f7::<(i8, T)>(f7::<(u8, T)>(x)) +} + +fn main() { + let y = f8::<()>(1); + assert_eq!(y, 348); +} From b9b969a260ca3f6ba5f4f7244905a53a60b391ef Mon Sep 17 00:00:00 2001 From: beetrees Date: Wed, 14 Sep 2022 11:43:02 +0100 Subject: [PATCH 15/15] Add a niche to `Duration`, unix `SystemTime`, and non-apple `Instant` --- library/core/src/time.rs | 98 +++++++++++++++++--------------- library/std/src/sys/unix/time.rs | 34 ++++++----- 2 files changed, 73 insertions(+), 59 deletions(-) diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 4f29ecc0fba8b..2b85d6e22258f 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -29,6 +29,20 @@ const NANOS_PER_MICRO: u32 = 1_000; const MILLIS_PER_SEC: u64 = 1_000; const MICROS_PER_SEC: u64 = 1_000_000; +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +#[rustc_layout_scalar_valid_range_end(999_999_999)] +struct Nanoseconds(u32); + +impl Default for Nanoseconds { + #[inline] + fn default() -> Self { + // SAFETY: 0 is within the valid range + unsafe { Nanoseconds(0) } + } +} + /// A `Duration` type to represent a span of time, typically used for system /// timeouts. /// @@ -71,7 +85,7 @@ const MICROS_PER_SEC: u64 = 1_000_000; #[cfg_attr(not(test), rustc_diagnostic_item = "Duration")] pub struct Duration { secs: u64, - nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC + nanos: Nanoseconds, // Always 0 <= nanos < NANOS_PER_SEC } impl Duration { @@ -188,7 +202,8 @@ impl Duration { None => panic!("overflow in Duration::new"), }; let nanos = nanos % NANOS_PER_SEC; - Duration { secs, nanos } + // SAFETY: nanos % NANOS_PER_SEC < NANOS_PER_SEC, therefore nanos is within the valid range + Duration { secs, nanos: unsafe { Nanoseconds(nanos) } } } /// Creates a new `Duration` from the specified number of whole seconds. @@ -208,7 +223,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] pub const fn from_secs(secs: u64) -> Duration { - Duration { secs, nanos: 0 } + Duration::new(secs, 0) } /// Creates a new `Duration` from the specified number of milliseconds. @@ -228,10 +243,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] pub const fn from_millis(millis: u64) -> Duration { - Duration { - secs: millis / MILLIS_PER_SEC, - nanos: ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI, - } + Duration::new(millis / MILLIS_PER_SEC, ((millis % MILLIS_PER_SEC) as u32) * NANOS_PER_MILLI) } /// Creates a new `Duration` from the specified number of microseconds. @@ -251,10 +263,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] pub const fn from_micros(micros: u64) -> Duration { - Duration { - secs: micros / MICROS_PER_SEC, - nanos: ((micros % MICROS_PER_SEC) as u32) * NANOS_PER_MICRO, - } + Duration::new(micros / MICROS_PER_SEC, ((micros % MICROS_PER_SEC) as u32) * NANOS_PER_MICRO) } /// Creates a new `Duration` from the specified number of nanoseconds. @@ -274,10 +283,7 @@ impl Duration { #[inline] #[rustc_const_stable(feature = "duration_consts", since = "1.32.0")] pub const fn from_nanos(nanos: u64) -> Duration { - Duration { - secs: nanos / (NANOS_PER_SEC as u64), - nanos: (nanos % (NANOS_PER_SEC as u64)) as u32, - } + Duration::new(nanos / (NANOS_PER_SEC as u64), (nanos % (NANOS_PER_SEC as u64)) as u32) } /// Returns true if this `Duration` spans no time. @@ -301,7 +307,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_zero", since = "1.53.0")] #[inline] pub const fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos == 0 + self.secs == 0 && self.nanos.0 == 0 } /// Returns the number of _whole_ seconds contained by this `Duration`. @@ -352,7 +358,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_millis(&self) -> u32 { - self.nanos / NANOS_PER_MILLI + self.nanos.0 / NANOS_PER_MILLI } /// Returns the fractional part of this `Duration`, in whole microseconds. @@ -375,7 +381,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_micros(&self) -> u32 { - self.nanos / NANOS_PER_MICRO + self.nanos.0 / NANOS_PER_MICRO } /// Returns the fractional part of this `Duration`, in nanoseconds. @@ -398,7 +404,7 @@ impl Duration { #[must_use] #[inline] pub const fn subsec_nanos(&self) -> u32 { - self.nanos + self.nanos.0 } /// Returns the total number of whole milliseconds contained by this `Duration`. @@ -416,7 +422,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_millis(&self) -> u128 { - self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos / NANOS_PER_MILLI) as u128 + self.secs as u128 * MILLIS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MILLI) as u128 } /// Returns the total number of whole microseconds contained by this `Duration`. @@ -434,7 +440,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_micros(&self) -> u128 { - self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos / NANOS_PER_MICRO) as u128 + self.secs as u128 * MICROS_PER_SEC as u128 + (self.nanos.0 / NANOS_PER_MICRO) as u128 } /// Returns the total number of nanoseconds contained by this `Duration`. @@ -452,7 +458,7 @@ impl Duration { #[must_use] #[inline] pub const fn as_nanos(&self) -> u128 { - self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos as u128 + self.secs as u128 * NANOS_PER_SEC as u128 + self.nanos.0 as u128 } /// Checked `Duration` addition. Computes `self + other`, returning [`None`] @@ -475,7 +481,7 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_add(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_add(rhs.secs) { - let mut nanos = self.nanos + rhs.nanos; + let mut nanos = self.nanos.0 + rhs.nanos.0; if nanos >= NANOS_PER_SEC { nanos -= NANOS_PER_SEC; if let Some(new_secs) = secs.checked_add(1) { @@ -485,7 +491,7 @@ impl Duration { } } debug_assert!(nanos < NANOS_PER_SEC); - Some(Duration { secs, nanos }) + Some(Duration::new(secs, nanos)) } else { None } @@ -535,16 +541,16 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_sub(self, rhs: Duration) -> Option { if let Some(mut secs) = self.secs.checked_sub(rhs.secs) { - let nanos = if self.nanos >= rhs.nanos { - self.nanos - rhs.nanos + let nanos = if self.nanos.0 >= rhs.nanos.0 { + self.nanos.0 - rhs.nanos.0 } else if let Some(sub_secs) = secs.checked_sub(1) { secs = sub_secs; - self.nanos + NANOS_PER_SEC - rhs.nanos + self.nanos.0 + NANOS_PER_SEC - rhs.nanos.0 } else { return None; }; debug_assert!(nanos < NANOS_PER_SEC); - Some(Duration { secs, nanos }) + Some(Duration::new(secs, nanos)) } else { None } @@ -593,13 +599,13 @@ impl Duration { #[rustc_const_stable(feature = "duration_consts_2", since = "1.58.0")] pub const fn checked_mul(self, rhs: u32) -> Option { // Multiply nanoseconds as u64, because it cannot overflow that way. - let total_nanos = self.nanos as u64 * rhs as u64; + let total_nanos = self.nanos.0 as u64 * rhs as u64; let extra_secs = total_nanos / (NANOS_PER_SEC as u64); let nanos = (total_nanos % (NANOS_PER_SEC as u64)) as u32; if let Some(s) = self.secs.checked_mul(rhs as u64) { if let Some(secs) = s.checked_add(extra_secs) { debug_assert!(nanos < NANOS_PER_SEC); - return Some(Duration { secs, nanos }); + return Some(Duration::new(secs, nanos)); } } None @@ -653,9 +659,9 @@ impl Duration { let secs = self.secs / (rhs as u64); let carry = self.secs - secs * (rhs as u64); let extra_nanos = carry * (NANOS_PER_SEC as u64) / (rhs as u64); - let nanos = self.nanos / rhs + (extra_nanos as u32); + let nanos = self.nanos.0 / rhs + (extra_nanos as u32); debug_assert!(nanos < NANOS_PER_SEC); - Some(Duration { secs, nanos }) + Some(Duration::new(secs, nanos)) } else { None } @@ -677,7 +683,7 @@ impl Duration { #[inline] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] pub const fn as_secs_f64(&self) -> f64 { - (self.secs as f64) + (self.nanos as f64) / (NANOS_PER_SEC as f64) + (self.secs as f64) + (self.nanos.0 as f64) / (NANOS_PER_SEC as f64) } /// Returns the number of seconds contained by this `Duration` as `f32`. @@ -696,7 +702,7 @@ impl Duration { #[inline] #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] pub const fn as_secs_f32(&self) -> f32 { - (self.secs as f32) + (self.nanos as f32) / (NANOS_PER_SEC as f32) + (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32) } /// Creates a new `Duration` from the specified number of seconds represented @@ -987,13 +993,13 @@ macro_rules! sum_durations { for entry in $iter { total_secs = total_secs.checked_add(entry.secs).expect("overflow in iter::sum over durations"); - total_nanos = match total_nanos.checked_add(entry.nanos as u64) { + total_nanos = match total_nanos.checked_add(entry.nanos.0 as u64) { Some(n) => n, None => { total_secs = total_secs .checked_add(total_nanos / NANOS_PER_SEC as u64) .expect("overflow in iter::sum over durations"); - (total_nanos % NANOS_PER_SEC as u64) + entry.nanos as u64 + (total_nanos % NANOS_PER_SEC as u64) + entry.nanos.0 as u64 } }; } @@ -1001,7 +1007,7 @@ macro_rules! sum_durations { .checked_add(total_nanos / NANOS_PER_SEC as u64) .expect("overflow in iter::sum over durations"); total_nanos = total_nanos % NANOS_PER_SEC as u64; - Duration { secs: total_secs, nanos: total_nanos as u32 } + Duration::new(total_secs, total_nanos as u32) }}; } @@ -1166,27 +1172,27 @@ impl fmt::Debug for Duration { let prefix = if f.sign_plus() { "+" } else { "" }; if self.secs > 0 { - fmt_decimal(f, self.secs, self.nanos, NANOS_PER_SEC / 10, prefix, "s") - } else if self.nanos >= NANOS_PER_MILLI { + fmt_decimal(f, self.secs, self.nanos.0, NANOS_PER_SEC / 10, prefix, "s") + } else if self.nanos.0 >= NANOS_PER_MILLI { fmt_decimal( f, - (self.nanos / NANOS_PER_MILLI) as u64, - self.nanos % NANOS_PER_MILLI, + (self.nanos.0 / NANOS_PER_MILLI) as u64, + self.nanos.0 % NANOS_PER_MILLI, NANOS_PER_MILLI / 10, prefix, "ms", ) - } else if self.nanos >= NANOS_PER_MICRO { + } else if self.nanos.0 >= NANOS_PER_MICRO { fmt_decimal( f, - (self.nanos / NANOS_PER_MICRO) as u64, - self.nanos % NANOS_PER_MICRO, + (self.nanos.0 / NANOS_PER_MICRO) as u64, + self.nanos.0 % NANOS_PER_MICRO, NANOS_PER_MICRO / 10, prefix, "µs", ) } else { - fmt_decimal(f, self.nanos as u64, 0, 1, prefix, "ns") + fmt_decimal(f, self.nanos.0 as u64, 0, 1, prefix, "ns") } } } @@ -1317,7 +1323,7 @@ macro_rules! try_from_secs { return Err(FromFloatSecsError { kind: FromFloatSecsErrorKind::OverflowOrNan }); }; - Ok(Duration { secs, nanos }) + Ok(Duration::new(secs, nanos)) }}; } diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index dff973f59d1a7..cca9c67670161 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -6,6 +6,12 @@ pub use self::inner::Instant; const NSEC_PER_SEC: u64 = 1_000_000_000; pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +#[rustc_layout_scalar_valid_range_end(999_999_999)] +struct Nanoseconds(u32); + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SystemTime { pub(in crate::sys::unix) t: Timespec, @@ -14,7 +20,7 @@ pub struct SystemTime { #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(in crate::sys::unix) struct Timespec { tv_sec: i64, - tv_nsec: i64, + tv_nsec: Nanoseconds, } impl SystemTime { @@ -46,18 +52,20 @@ impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec) + .field("tv_nsec", &self.t.tv_nsec.0) .finish() } } impl Timespec { pub const fn zero() -> Timespec { - Timespec { tv_sec: 0, tv_nsec: 0 } + Timespec::new(0, 0) } - fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { - Timespec { tv_sec, tv_nsec } + const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + // SAFETY: The assert above checks tv_nsec is within the valid range + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } } pub fn sub_timespec(&self, other: &Timespec) -> Result { @@ -75,12 +83,12 @@ impl Timespec { // // Ideally this code could be rearranged such that it more // directly expresses the lower-cost behavior we want from it. - let (secs, nsec) = if self.tv_nsec >= other.tv_nsec { - ((self.tv_sec - other.tv_sec) as u64, (self.tv_nsec - other.tv_nsec) as u32) + let (secs, nsec) = if self.tv_nsec.0 >= other.tv_nsec.0 { + ((self.tv_sec - other.tv_sec) as u64, self.tv_nsec.0 - other.tv_nsec.0) } else { ( (self.tv_sec - other.tv_sec - 1) as u64, - self.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.tv_nsec as u32, + self.tv_nsec.0 + (NSEC_PER_SEC as u32) - other.tv_nsec.0, ) }; @@ -102,7 +110,7 @@ impl Timespec { // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.tv_nsec as u32; + let mut nsec = other.subsec_nanos() + self.tv_nsec.0; if nsec >= NSEC_PER_SEC as u32 { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; @@ -118,7 +126,7 @@ impl Timespec { .and_then(|secs| self.tv_sec.checked_sub(secs))?; // Similar to above, nanos can't overflow. - let mut nsec = self.tv_nsec as i32 - other.subsec_nanos() as i32; + let mut nsec = self.tv_nsec.0 as i32 - other.subsec_nanos() as i32; if nsec < 0 { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; @@ -130,7 +138,7 @@ impl Timespec { pub fn to_timespec(&self) -> Option { Some(libc::timespec { tv_sec: self.tv_sec.try_into().ok()?, - tv_nsec: self.tv_nsec.try_into().ok()?, + tv_nsec: self.tv_nsec.0.try_into().ok()?, }) } } @@ -293,7 +301,7 @@ mod inner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Instant") .field("tv_sec", &self.t.tv_sec) - .field("tv_nsec", &self.t.tv_nsec) + .field("tv_nsec", &self.t.tv_nsec.0) .finish() } } @@ -334,7 +342,7 @@ mod inner { let mut t = MaybeUninit::uninit(); cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); let t = unsafe { t.assume_init() }; - return Timespec { tv_sec: t.tv_sec, tv_nsec: t.tv_nsec as i64 }; + return Timespec::new(t.tv_sec, t.tv_nsec as i64); } }