From 3a6af84fca1b41614887748e9fdb0e2b2a13fb96 Mon Sep 17 00:00:00 2001 From: Elliot Roberts Date: Tue, 12 Apr 2022 07:46:07 -0700 Subject: [PATCH 01/79] change std::process to drop supplementary groups based on CAP_SETGID --- library/std/src/os/unix/process.rs | 7 +++++++ .../std/src/sys/pal/unix/process/process_unix.rs | 14 +++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index e45457b2e42b4..72ea54bd77203 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -39,6 +39,13 @@ pub trait CommandExt: Sealed { /// Sets the child process's user ID. This translates to a /// `setuid` call in the child process. Failure in the `setuid` /// call will cause the spawn to fail. + /// + /// # Notes + /// + /// This will also trigger a call to `setgroups(0, NULL)` in the child + /// process if no groups have been specified. + /// This removes supplementary groups that might have given the child + /// unwanted permissions. #[stable(feature = "rust1", since = "1.0.0")] fn uid(&mut self, id: UserId) -> &mut process::Command; diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index 97cbd1929d329..f017d39d804aa 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -330,14 +330,22 @@ impl Command { if let Some(u) = self.get_uid() { // When dropping privileges from root, the `setgroups` call // will remove any extraneous groups. We only drop groups - // if the current uid is 0 and we weren't given an explicit + // if we have CAP_SETGID and we weren't given an explicit // set of groups. If we don't call this, then even though our // uid has dropped, we may still have groups that enable us to // do super-user things. //FIXME: Redox kernel does not support setgroups yet #[cfg(not(target_os = "redox"))] - if libc::getuid() == 0 && self.get_groups().is_none() { - cvt(libc::setgroups(0, crate::ptr::null()))?; + if self.get_groups().is_none() { + let res = cvt(libc::setgroups(0, crate::ptr::null())); + if let Err(e) = res { + // Here we ignore the case of not having CAP_SETGID. + // An alternative would be to require CAP_SETGID (in + // addition to CAP_SETUID) for setting the UID. + if e.raw_os_error() != Some(libc::EPERM) { + return Err(e.into()); + } + } } cvt(libc::setuid(u as uid_t))?; } From bf62d5913a702754d46a0e9210fcf608deba63af Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 9 Feb 2024 16:16:22 +1100 Subject: [PATCH 02/79] Give `TRACK_DIAGNOSTIC` a return value. This means `DiagCtxtInner::emit_diagnostic` can return its result directly, rather than having to modify a local variable. --- compiler/rustc_errors/src/lib.rs | 24 ++++++++++++----------- compiler/rustc_interface/src/callbacks.rs | 10 +++++----- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0a533833e64bd..ce24f1ceaac7b 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -526,12 +526,15 @@ pub enum StashKey { UndeterminedMacroResolution, } -fn default_track_diagnostic(diag: DiagInner, f: &mut dyn FnMut(DiagInner)) { +fn default_track_diagnostic(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { (*f)(diag) } -pub static TRACK_DIAGNOSTIC: AtomicRef = - AtomicRef::new(&(default_track_diagnostic as _)); +/// Diagnostics emitted by `DiagCtxtInner::emit_diagnostic` are passed through this function. Used +/// for tracking by incremental, to replay diagnostics as necessary. +pub static TRACK_DIAGNOSTIC: AtomicRef< + fn(DiagInner, &mut dyn FnMut(DiagInner) -> Option) -> Option, +> = AtomicRef::new(&(default_track_diagnostic as _)); #[derive(Copy, Clone, Default)] pub struct DiagCtxtFlags { @@ -1406,19 +1409,18 @@ impl DiagCtxtInner { } Warning if !self.flags.can_emit_warnings => { if diagnostic.has_future_breakage() { - (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); } return None; } Allow | Expect(_) => { - (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); return None; } _ => {} } - let mut guaranteed = None; - (*TRACK_DIAGNOSTIC)(diagnostic, &mut |mut diagnostic| { + TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| { if let Some(code) = diagnostic.code { self.emitted_diagnostic_codes.insert(code); } @@ -1481,17 +1483,17 @@ impl DiagCtxtInner { // `ErrorGuaranteed` for errors and lint errors originates. #[allow(deprecated)] let guar = ErrorGuaranteed::unchecked_error_guaranteed(); - guaranteed = Some(guar); if is_lint { self.lint_err_guars.push(guar); } else { self.err_guars.push(guar); } self.panic_if_treat_err_as_bug(); + Some(guar) + } else { + None } - }); - - guaranteed + }) } fn treat_err_as_bug(&self) -> bool { diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index f44ae705a3c27..a27f73789cdef 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -29,7 +29,7 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { /// This is a callback from `rustc_errors` as it cannot access the implicit state /// in `rustc_middle` otherwise. It is used when diagnostic messages are /// emitted and stores them in the current query, if there is one. -fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner)) { +fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { tls::with_context_opt(|icx| { if let Some(icx) = icx { if let Some(diagnostics) = icx.diagnostics { @@ -38,11 +38,11 @@ fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner)) { // Diagnostics are tracked, we can ignore the dependency. let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() }; - return tls::enter_context(&icx, move || (*f)(diagnostic)); + tls::enter_context(&icx, move || (*f)(diagnostic)) + } else { + // In any other case, invoke diagnostics anyway. + (*f)(diagnostic) } - - // In any other case, invoke diagnostics anyway. - (*f)(diagnostic); }) } From ecd3718bc0b3f823b96d6949ba22f77cfbeff5c1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 13 Feb 2024 12:20:38 +1100 Subject: [PATCH 03/79] Inline and remove `Level::get_diagnostic_id`. It has a single call site, and this will enable subsequent refactorings. --- compiler/rustc_errors/src/lib.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index ce24f1ceaac7b..0c8308b6b09b6 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1356,17 +1356,17 @@ impl DiagCtxtInner { fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option { assert!(diagnostic.level.can_be_top_or_sub().0); - if let Some(expectation_id) = diagnostic.level.get_expectation_id() { + if let Expect(expect_id) | ForceWarning(Some(expect_id)) = diagnostic.level { // The `LintExpectationId` can be stable or unstable depending on when it was created. // Diagnostics created before the definition of `HirId`s are unstable and can not yet // be stored. Instead, they are buffered until the `LintExpectationId` is replaced by // a stable one by the `LintLevelsBuilder`. - if let LintExpectationId::Unstable { .. } = expectation_id { + if let LintExpectationId::Unstable { .. } = expect_id { self.unstable_expect_diagnostics.push(diagnostic); return None; } self.suppressed_expected_diag = true; - self.fulfilled_expectations.insert(expectation_id.normalize()); + self.fulfilled_expectations.insert(expect_id.normalize()); } if diagnostic.has_future_breakage() { @@ -1794,13 +1794,6 @@ impl Level { matches!(*self, FailureNote) } - pub fn get_expectation_id(&self) -> Option { - match self { - Expect(id) | ForceWarning(Some(id)) => Some(*id), - _ => None, - } - } - // Can this level be used in a top-level diagnostic message and/or a // subdiagnostic message? fn can_be_top_or_sub(&self) -> (bool, bool) { From 272e60bd3ef5ea2424b70b0f4ec821f38fa3dc79 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 13 Feb 2024 13:08:24 +1100 Subject: [PATCH 04/79] Move `DelayedBug` handling into the `match`. It results in a tiny bit of duplication (another `self.treat_next_err_as_bug()` condition) but I think it's worth it to get more code into the main `match`. --- compiler/rustc_errors/src/lib.rs | 51 ++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0c8308b6b09b6..2280d3a24172d 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1377,35 +1377,40 @@ impl DiagCtxtInner { self.future_breakage_diagnostics.push(diagnostic.clone()); } - // Note that because this comes before the `match` below, - // `-Zeagerly-emit-delayed-bugs` continues to work even after we've - // issued an error and stopped recording new delayed bugs. - if diagnostic.level == DelayedBug && self.flags.eagerly_emit_delayed_bugs { - diagnostic.level = Error; - } - match diagnostic.level { - // This must come after the possible promotion of `DelayedBug` to - // `Error` above. Fatal | Error if self.treat_next_err_as_bug() => { + // `Fatal` and `Error` can be promoted to `Bug`. diagnostic.level = Bug; } DelayedBug => { - // If we have already emitted at least one error, we don't need - // to record the delayed bug, because it'll never be used. - return if let Some(guar) = self.has_errors() { - Some(guar) + // Note that because we check these conditions first, + // `-Zeagerly-emit-delayed-bugs` and `-Ztreat-err-as-bug` + // continue to work even after we've issued an error and + // stopped recording new delayed bugs. + if self.flags.eagerly_emit_delayed_bugs { + // `DelayedBug` can be promoted to `Error` or `Bug`. + if self.treat_next_err_as_bug() { + diagnostic.level = Bug; + } else { + diagnostic.level = Error; + } } else { - let backtrace = std::backtrace::Backtrace::capture(); - // This `unchecked_error_guaranteed` is valid. It is where the - // `ErrorGuaranteed` for delayed bugs originates. See - // `DiagCtxtInner::drop`. - #[allow(deprecated)] - let guar = ErrorGuaranteed::unchecked_error_guaranteed(); - self.delayed_bugs - .push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar)); - Some(guar) - }; + // If we have already emitted at least one error, we don't need + // to record the delayed bug, because it'll never be used. + return if let Some(guar) = self.has_errors() { + Some(guar) + } else { + let backtrace = std::backtrace::Backtrace::capture(); + // This `unchecked_error_guaranteed` is valid. It is where the + // `ErrorGuaranteed` for delayed bugs originates. See + // `DiagCtxtInner::drop`. + #[allow(deprecated)] + let guar = ErrorGuaranteed::unchecked_error_guaranteed(); + self.delayed_bugs + .push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar)); + Some(guar) + }; + } } Warning if !self.flags.can_emit_warnings => { if diagnostic.has_future_breakage() { From c81767e7cb06dacc7bb9a5ec0b746b0c774e0aa1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 13 Feb 2024 13:14:41 +1100 Subject: [PATCH 05/79] Reorder `has_future_breakage` handling. This will enable additional refactorings. --- compiler/rustc_errors/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 2280d3a24172d..e073520870905 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1356,6 +1356,14 @@ impl DiagCtxtInner { fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option { assert!(diagnostic.level.can_be_top_or_sub().0); + if diagnostic.has_future_breakage() { + // Future breakages aren't emitted if they're Level::Allow, + // but they still need to be constructed and stashed below, + // so they'll trigger the must_produce_diag check. + self.suppressed_expected_diag = true; + self.future_breakage_diagnostics.push(diagnostic.clone()); + } + if let Expect(expect_id) | ForceWarning(Some(expect_id)) = diagnostic.level { // The `LintExpectationId` can be stable or unstable depending on when it was created. // Diagnostics created before the definition of `HirId`s are unstable and can not yet @@ -1369,14 +1377,6 @@ impl DiagCtxtInner { self.fulfilled_expectations.insert(expect_id.normalize()); } - if diagnostic.has_future_breakage() { - // Future breakages aren't emitted if they're Level::Allow, - // but they still need to be constructed and stashed below, - // so they'll trigger the must_produce_diag check. - self.suppressed_expected_diag = true; - self.future_breakage_diagnostics.push(diagnostic.clone()); - } - match diagnostic.level { Fatal | Error if self.treat_next_err_as_bug() => { // `Fatal` and `Error` can be promoted to `Bug`. From aec4bdb0a2802765ee90e4f59c9173f94f28f2eb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 13 Feb 2024 14:20:41 +1100 Subject: [PATCH 06/79] Move `Expect`/`ForceWarning` handling into the `match`. Note that `self.suppressed_expected_diag` is no longer set for `ForceWarning`, which is good. Nor is `TRACK_DIAGNOSTIC` called for `Allow`, which is also good. --- compiler/rustc_errors/src/lib.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index e073520870905..0f3999062913f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1364,19 +1364,6 @@ impl DiagCtxtInner { self.future_breakage_diagnostics.push(diagnostic.clone()); } - if let Expect(expect_id) | ForceWarning(Some(expect_id)) = diagnostic.level { - // The `LintExpectationId` can be stable or unstable depending on when it was created. - // Diagnostics created before the definition of `HirId`s are unstable and can not yet - // be stored. Instead, they are buffered until the `LintExpectationId` is replaced by - // a stable one by the `LintLevelsBuilder`. - if let LintExpectationId::Unstable { .. } = expect_id { - self.unstable_expect_diagnostics.push(diagnostic); - return None; - } - self.suppressed_expected_diag = true; - self.fulfilled_expectations.insert(expect_id.normalize()); - } - match diagnostic.level { Fatal | Error if self.treat_next_err_as_bug() => { // `Fatal` and `Error` can be promoted to `Bug`. @@ -1418,10 +1405,25 @@ impl DiagCtxtInner { } return None; } - Allow | Expect(_) => { - TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + Allow => { return None; } + Expect(expect_id) | ForceWarning(Some(expect_id)) => { + // Diagnostics created before the definition of `HirId`s are + // unstable and can not yet be stored. Instead, they are + // buffered until the `LintExpectationId` is replaced by a + // stable one by the `LintLevelsBuilder`. + if let LintExpectationId::Unstable { .. } = expect_id { + self.unstable_expect_diagnostics.push(diagnostic); + return None; + } + self.fulfilled_expectations.insert(expect_id.normalize()); + if let Expect(_) = diagnostic.level { + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; + return None; + } + } _ => {} } From a7d926265f25abfd05b13a3a0144f2ad9dd02dd7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 13 Feb 2024 14:25:29 +1100 Subject: [PATCH 07/79] Add comments about `TRACK_DIAGNOSTIC` use. Also add an assertion for the levels allowed with `has_future_breakage`. --- compiler/rustc_errors/src/lib.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0f3999062913f..738016f9b999c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1360,10 +1360,13 @@ impl DiagCtxtInner { // Future breakages aren't emitted if they're Level::Allow, // but they still need to be constructed and stashed below, // so they'll trigger the must_produce_diag check. - self.suppressed_expected_diag = true; + assert!(matches!(diagnostic.level, Error | Warning | Allow)); self.future_breakage_diagnostics.push(diagnostic.clone()); } + // We call TRACK_DIAGNOSTIC with an empty closure for the cases that + // return early *and* have some kind of side-effect, except where + // noted. match diagnostic.level { Fatal | Error if self.treat_next_err_as_bug() => { // `Fatal` and `Error` can be promoted to `Bug`. @@ -1387,6 +1390,9 @@ impl DiagCtxtInner { return if let Some(guar) = self.has_errors() { Some(guar) } else { + // No `TRACK_DIAGNOSTIC` call is needed, because the + // incremental session is deleted if there is a delayed + // bug. This also saves us from cloning the diagnostic. let backtrace = std::backtrace::Backtrace::capture(); // This `unchecked_error_guaranteed` is valid. It is where the // `ErrorGuaranteed` for delayed bugs originates. See @@ -1401,11 +1407,17 @@ impl DiagCtxtInner { } Warning if !self.flags.can_emit_warnings => { if diagnostic.has_future_breakage() { + // The side-effect is at the top of this method. TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); } return None; } Allow => { + if diagnostic.has_future_breakage() { + // The side-effect is at the top of this method. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; + } return None; } Expect(expect_id) | ForceWarning(Some(expect_id)) => { @@ -1414,6 +1426,9 @@ impl DiagCtxtInner { // buffered until the `LintExpectationId` is replaced by a // stable one by the `LintLevelsBuilder`. if let LintExpectationId::Unstable { .. } = expect_id { + // We don't call TRACK_DIAGNOSTIC because we wait for the + // unstable ID to be updated, whereupon the diagnostic will + // be passed into this method again. self.unstable_expect_diagnostics.push(diagnostic); return None; } From 7ef605be3fa24f93194a3faf4f75d3a05ceca78a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 1 Mar 2024 13:46:00 +1100 Subject: [PATCH 08/79] Make the `match` in `emit_diagnostic` complete. This match is complex enough that it's a good idea to enumerate every variant. This also means `can_be_top_or_sub` can just be `can_be_subdiag`. --- compiler/rustc_errors/src/emitter.rs | 2 +- compiler/rustc_errors/src/lib.rs | 41 ++++++++++++++++------------ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 5637c05d04c69..212bda5ff0320 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2104,7 +2104,7 @@ impl HumanEmitter { } if !self.short_message { for child in children { - assert!(child.level.can_be_top_or_sub().1); + assert!(child.level.can_be_subdiag()); let span = &child.span; if let Err(err) = self.emit_messages_default_inner( span, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 738016f9b999c..069ac863d9939 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1354,8 +1354,6 @@ impl DiagCtxtInner { // Return value is only `Some` if the level is `Error` or `DelayedBug`. fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option { - assert!(diagnostic.level.can_be_top_or_sub().0); - if diagnostic.has_future_breakage() { // Future breakages aren't emitted if they're Level::Allow, // but they still need to be constructed and stashed below, @@ -1368,9 +1366,12 @@ impl DiagCtxtInner { // return early *and* have some kind of side-effect, except where // noted. match diagnostic.level { - Fatal | Error if self.treat_next_err_as_bug() => { - // `Fatal` and `Error` can be promoted to `Bug`. - diagnostic.level = Bug; + Bug => {} + Fatal | Error => { + if self.treat_next_err_as_bug() { + // `Fatal` and `Error` can be promoted to `Bug`. + diagnostic.level = Bug; + } } DelayedBug => { // Note that because we check these conditions first, @@ -1405,14 +1406,21 @@ impl DiagCtxtInner { }; } } - Warning if !self.flags.can_emit_warnings => { - if diagnostic.has_future_breakage() { - // The side-effect is at the top of this method. - TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + ForceWarning(None) => {} // `ForceWarning(Some(...))` is below, with `Expect` + Warning => { + if !self.flags.can_emit_warnings { + // We are not emitting warnings. + if diagnostic.has_future_breakage() { + // The side-effect is at the top of this method. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + } + return None; } - return None; } + Note | Help | FailureNote => {} + OnceNote | OnceHelp => panic!("bad level: {:?}", diagnostic.level), Allow => { + // Nothing emitted for allowed lints. if diagnostic.has_future_breakage() { // The side-effect is at the top of this method. TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); @@ -1434,12 +1442,12 @@ impl DiagCtxtInner { } self.fulfilled_expectations.insert(expect_id.normalize()); if let Expect(_) = diagnostic.level { + // Nothing emitted here for expected lints. TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); self.suppressed_expected_diag = true; return None; } } - _ => {} } TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| { @@ -1816,16 +1824,13 @@ impl Level { matches!(*self, FailureNote) } - // Can this level be used in a top-level diagnostic message and/or a - // subdiagnostic message? - fn can_be_top_or_sub(&self) -> (bool, bool) { + // Can this level be used in a subdiagnostic message? + fn can_be_subdiag(&self) -> bool { match self { Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow - | Expect(_) => (true, false), - - Warning | Note | Help => (true, true), + | Expect(_) => false, - OnceNote | OnceHelp => (false, true), + Warning | Note | Help | OnceNote | OnceHelp => true, } } } From aa692a577eee7fa1aaa72d29efeb860bbae6572f Mon Sep 17 00:00:00 2001 From: Kai Luo Date: Tue, 24 Oct 2023 14:59:05 +0800 Subject: [PATCH 09/79] [AIX] Remove AixLinker's debuginfo() implementation `-s` option doesn't perfectly fit into debuginfo()'s semantics and may unexpectedly remove metadata in shared libraries. Remove the implementation and suggest user to use `strip` utility instead. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index e52efd8695558..9bdff2abee9ee 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1639,16 +1639,7 @@ impl<'a> Linker for AixLinker<'a> { fn ehcont_guard(&mut self) {} - fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { - match strip { - Strip::None => {} - // FIXME: -s strips the symbol table, line number information - // and relocation information. - Strip::Debuginfo | Strip::Symbols => { - self.cmd.arg("-s"); - } - } - } + fn debuginfo(&mut self, _: Strip, _: &[PathBuf]) {} fn no_crt_objects(&mut self) {} From e74e6e767d4b5cc1f033cee5b5ef4704dd8fee31 Mon Sep 17 00:00:00 2001 From: Kai Luo Date: Tue, 5 Mar 2024 15:37:37 +0800 Subject: [PATCH 10/79] Rebased --- compiler/rustc_codegen_ssa/src/back/link.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index fcb3602b7349c..b52e6242d992d 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1080,6 +1080,21 @@ fn link_natively<'a>( } } + if sess.target.is_like_aix { + let stripcmd = "/usr/bin/strip"; + match strip { + Strip::Debuginfo => { + // FIXME: AIX's strip utility only offers option to strip line number information. + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-l")) + } + Strip::Symbols => { + // Must be noted this option removes symbol __aix_rust_metadata and thus removes .info section which contains metadata. + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-r")) + } + Strip::None => {} + } + } + Ok(()) } From b1c390989fc8b74aa36f445cd66316a8ca96455d Mon Sep 17 00:00:00 2001 From: Kai Luo Date: Tue, 5 Mar 2024 15:42:31 +0800 Subject: [PATCH 11/79] Adjust wording --- compiler/rustc_codegen_ssa/src/back/link.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index b52e6242d992d..a8d6963294df3 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1088,7 +1088,7 @@ fn link_natively<'a>( strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-l")) } Strip::Symbols => { - // Must be noted this option removes symbol __aix_rust_metadata and thus removes .info section which contains metadata. + // Must be noted this option might remove symbol __aix_rust_metadata and thus removes .info section which contains metadata. strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-r")) } Strip::None => {} From 408c0ea2162b9892540a5b3916ddcac7713de8c3 Mon Sep 17 00:00:00 2001 From: Antoine PLASKOWSKI Date: Tue, 25 Jul 2023 05:59:22 +0200 Subject: [PATCH 12/79] unix time module now return result --- library/std/src/sys/pal/unix/fs.rs | 30 +++++++-------- library/std/src/sys/pal/unix/time.rs | 56 ++++++++++------------------ 2 files changed, 35 insertions(+), 51 deletions(-) diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index c75323ef7757a..87646bfdfe73a 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -463,15 +463,15 @@ impl FileAttr { #[cfg(target_os = "netbsd")] impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64)) + SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64)) + SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64)) + SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64) } } @@ -503,16 +503,16 @@ impl FileAttr { #[cfg(target_pointer_width = "32")] cfg_has_statx! { if let Some(mtime) = self.stx_mtime() { - return Ok(SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64)); + return SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64); } } - Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64)) + SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64) } #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtime as i64, 0)) + SystemTime::new(self.stat.st_mtime as i64, 0) } #[cfg(any(target_os = "horizon", target_os = "hurd"))] @@ -531,16 +531,16 @@ impl FileAttr { #[cfg(target_pointer_width = "32")] cfg_has_statx! { if let Some(atime) = self.stx_atime() { - return Ok(SystemTime::new(atime.tv_sec, atime.tv_nsec as i64)); + return SystemTime::new(atime.tv_sec, atime.tv_nsec as i64); } } - Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64)) + SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64) } #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atime as i64, 0)) + SystemTime::new(self.stat.st_atime as i64, 0) } #[cfg(any(target_os = "horizon", target_os = "hurd"))] @@ -557,7 +557,7 @@ impl FileAttr { target_os = "watchos", ))] pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64)) + SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64) } #[cfg(not(any( @@ -573,7 +573,7 @@ impl FileAttr { cfg_has_statx! { if let Some(ext) = &self.statx_extra_fields { return if (ext.stx_mask & libc::STATX_BTIME) != 0 { - Ok(SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64)) + SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64) } else { Err(io::const_io_error!( io::ErrorKind::Uncategorized, @@ -592,22 +592,22 @@ impl FileAttr { #[cfg(target_os = "vita")] pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_ctime as i64, 0)) + SystemTime::new(self.stat.st_ctime as i64, 0) } } #[cfg(target_os = "nto")] impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec)) + SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec)) + SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec)) + SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec) } } diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index f62eb828ee5d4..911cf29843e7b 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -1,5 +1,5 @@ -use crate::fmt; use crate::time::Duration; +use crate::{fmt, io}; const NSEC_PER_SEC: u64 = 1_000_000_000; pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; @@ -34,8 +34,8 @@ pub(crate) struct Timespec { impl SystemTime { #[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))] - pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { - SystemTime { t: Timespec::new(tv_sec, tv_nsec) } + pub fn new(tv_sec: i64, tv_nsec: i64) -> Result { + Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? }) } pub fn now() -> SystemTime { @@ -55,12 +55,6 @@ impl SystemTime { } } -impl From for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec::from(t) } - } -} - impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") @@ -71,11 +65,15 @@ impl fmt::Debug for SystemTime { } impl Timespec { + const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec { + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } + } + pub const fn zero() -> Timespec { - Timespec::new(0, 0) + unsafe { Self::new_unchecked(0, 0) } } - const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + const fn new(tv_sec: i64, tv_nsec: i64) -> Result { // On Apple OS, dates before epoch are represented differently than on other // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1` // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and @@ -100,9 +98,11 @@ impl Timespec { } else { (tv_sec, tv_nsec) }; - 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) } } + if tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64 { + Ok(unsafe { Self::new_unchecked(tv_sec, tv_nsec) }) + } else { + Err(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid timestamp")) + } } pub fn now(clock: libc::clockid_t) -> Timespec { @@ -126,13 +126,15 @@ impl Timespec { if let Some(clock_gettime64) = __clock_gettime64.get() { let mut t = MaybeUninit::uninit(); cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); - return Timespec::from(unsafe { t.assume_init() }); + let t = unsafe { t.assume_init() }; + return Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap(); } } let mut t = MaybeUninit::uninit(); cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); - Timespec::from(unsafe { t.assume_init() }) + let t = unsafe { t.assume_init() }; + Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap() } pub fn sub_timespec(&self, other: &Timespec) -> Result { @@ -178,7 +180,7 @@ impl Timespec { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; } - Some(Timespec::new(secs, nsec.into())) + Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) }) } pub fn checked_sub_duration(&self, other: &Duration) -> Option { @@ -190,7 +192,7 @@ impl Timespec { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; } - Some(Timespec::new(secs, nsec.into())) + Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) }) } #[allow(dead_code)] @@ -226,12 +228,6 @@ impl Timespec { } } -impl From for Timespec { - fn from(t: libc::timespec) -> Timespec { - Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) - } -} - #[cfg(all( target_os = "linux", target_env = "gnu", @@ -260,18 +256,6 @@ impl __timespec64 { } } -#[cfg(all( - target_os = "linux", - target_env = "gnu", - target_pointer_width = "32", - not(target_arch = "riscv32") -))] -impl From<__timespec64> for Timespec { - fn from(t: __timespec64) -> Timespec { - Timespec::new(t.tv_sec, t.tv_nsec.into()) - } -} - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant { t: Timespec, From 71080dd1d45007e9f806bb913cae47531c4dee12 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Sat, 2 Mar 2024 09:55:06 -0500 Subject: [PATCH 13/79] Document how removing a type's field can be bad and what to do instead Related to #119645 --- compiler/rustc_lint_defs/src/builtin.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 94f8bbe2437f8..54b86ec39ab19 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -702,6 +702,20 @@ declare_lint! { /// `PhantomData`. /// /// Otherwise consider removing the unused code. + /// + /// ### Limitations + /// + /// Removing fields that are only used for side-effects and never + /// read will result in behavioral changes. Examples of this + /// include: + /// + /// - If a field's value performs an action when it is dropped. + /// - If a field's type does not implement an auto trait + /// (e.g. `Send`, `Sync`, `Unpin`). + /// + /// For side-effects from dropping field values, this lint should + /// be allowed on those fields. For side-effects from containing + /// field types, `PhantomData` should be used. pub DEAD_CODE, Warn, "detect unused, unexported items" From 4b76fac3020b27c0cd3cf8b13ff91ee2d26e22ad Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sat, 9 Mar 2024 21:27:02 +0800 Subject: [PATCH 14/79] Document some builtin impls in the next solver --- compiler/rustc_target/src/abi/mod.rs | 1 + .../src/solve/trait_goals.rs | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 24e49ff648f2f..8d1c7c77bb698 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -121,6 +121,7 @@ impl<'a> Layout<'a> { /// /// Currently, that means that the type is pointer-sized, pointer-aligned, /// and has a initialized (non-union), scalar ABI. + // Please also update compiler/rustc_trait_selection/src/solve/trait_goals.rs if the criteria changes pub fn is_pointer_like(self, data_layout: &TargetDataLayout) -> bool { self.size() == data_layout.pointer_size && self.align().abi == data_layout.pointer_align.abi diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 80198ba39f9be..9c2a02a571786 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -188,6 +188,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } + /// ```rust,ignore (not valid rust syntax) + /// impl Sized for u*, i*, bool, f*, FnPtr, FnDef, *(const/mut) T, char, &mut? T, [T; N], dyn* Trait, ! + /// + /// impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized + /// + /// impl Sized for Adt where T: Sized forall T in field types + /// ``` + /// + /// note that `[T; N]` is unconditionally sized since `T: Sized` is required for the array type to be + /// well-formed. fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -202,6 +212,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } + /// ```rust,ignore (not valid rust syntax) + /// impl Copy/Clone for FnDef, FnPtr + /// + /// impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone + /// + /// impl Copy/Clone for Closure where T: Copy/Clone forall T in upvars + /// + /// // only when `coroutine_clone` is enabled and the coroutine is movable + /// impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) + /// + /// impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types + /// ``` + /// + /// Some built-in types don't have built-in impls because they can be implemented within the standard library. fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -216,6 +240,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } + /// Implements `PointerLike` for types that are pointer-sized, pointer-aligned, + /// and have a initialized (non-union), scalar ABI. + // Please also update compiler/rustc_target/src/abi/mod.rs if the criteria changes fn consider_builtin_pointer_like_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -245,6 +272,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } } + /// ```rust,ignore (not valid rust syntax) + /// impl FnPtr for FnPtr {} + /// impl !FnPtr for T where T != FnPtr && T is rigid {} + /// ``` + /// + /// Note: see [`Ty::is_known_rigid`] for what it means for the type to be rigid. fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -375,6 +408,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } } + /// ```rust, ignore (not valid rust syntax) + /// impl Tuple for () {} + /// impl Tuple for (T1,) {} + /// impl Tuple for (T1, T2) {} + /// impl Tuple for (T1, .., Tn) {} + /// ``` fn consider_builtin_tuple_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, From 1678c5c32810978da826b1c53aada6b9197144b1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Mar 2024 17:42:41 +0100 Subject: [PATCH 15/79] add_retag: fix comment that does not match the code --- compiler/rustc_mir_transform/src/add_retag.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 430d9572e7594..6f668aa4ce850 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -118,7 +118,7 @@ impl<'tcx> MirPass<'tcx> for AddRetag { } // PART 3 - // Add retag after assignments where data "enters" this function: the RHS is behind a deref and the LHS is not. + // Add retag after assignments. for block_data in basic_blocks { // We want to insert statements as we iterate. To this end, we // iterate backwards using indices. From a82587c1d44ac94fb95fb40989bcc2536fd13b9f Mon Sep 17 00:00:00 2001 From: dylni <46035563+dylni@users.noreply.github.com> Date: Sat, 9 Mar 2024 11:42:56 -0500 Subject: [PATCH 16/79] Avoid closing invalid handles --- library/std/src/os/windows/io/handle.rs | 68 +++++++++++++++++-------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 458c3bb036d8d..6461c90de6c19 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -7,7 +7,7 @@ use crate::fmt; use crate::fs; use crate::io; use crate::marker::PhantomData; -use crate::mem::forget; +use crate::mem::{forget, ManuallyDrop}; use crate::ptr; use crate::sys; use crate::sys::cvt; @@ -91,7 +91,7 @@ pub struct OwnedHandle { #[repr(transparent)] #[stable(feature = "io_safety", since = "1.63.0")] #[derive(Debug)] -pub struct HandleOrNull(OwnedHandle); +pub struct HandleOrNull(RawHandle); /// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used /// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses @@ -110,7 +110,7 @@ pub struct HandleOrNull(OwnedHandle); #[repr(transparent)] #[stable(feature = "io_safety", since = "1.63.0")] #[derive(Debug)] -pub struct HandleOrInvalid(OwnedHandle); +pub struct HandleOrInvalid(RawHandle); // The Windows [`HANDLE`] type may be transferred across and shared between // thread boundaries (despite containing a `*mut void`, which in general isn't @@ -163,15 +163,24 @@ impl TryFrom for OwnedHandle { #[inline] fn try_from(handle_or_null: HandleOrNull) -> Result { - let owned_handle = handle_or_null.0; - if owned_handle.handle.is_null() { - // Don't call `CloseHandle`; it'd be harmless, except that it could - // overwrite the `GetLastError` error. - forget(owned_handle); - - Err(NullHandleError(())) + let handle_or_null = ManuallyDrop::new(handle_or_null); + if handle_or_null.is_valid() { + // SAFETY: The handle is not null. + Ok(unsafe { OwnedHandle::from_raw_handle(handle_or_null.0) }) } else { - Ok(owned_handle) + Err(NullHandleError(())) + } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl Drop for HandleOrNull { + #[inline] + fn drop(&mut self) { + if self.is_valid() { + unsafe { + let _ = sys::c::CloseHandle(self.0); + } } } } @@ -232,15 +241,24 @@ impl TryFrom for OwnedHandle { #[inline] fn try_from(handle_or_invalid: HandleOrInvalid) -> Result { - let owned_handle = handle_or_invalid.0; - if owned_handle.handle == sys::c::INVALID_HANDLE_VALUE { - // Don't call `CloseHandle`; it'd be harmless, except that it could - // overwrite the `GetLastError` error. - forget(owned_handle); - - Err(InvalidHandleError(())) + let handle_or_invalid = ManuallyDrop::new(handle_or_invalid); + if handle_or_invalid.is_valid() { + // SAFETY: The handle is not invalid. + Ok(unsafe { OwnedHandle::from_raw_handle(handle_or_invalid.0) }) } else { - Ok(owned_handle) + Err(InvalidHandleError(())) + } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl Drop for HandleOrInvalid { + #[inline] + fn drop(&mut self) { + if self.is_valid() { + unsafe { + let _ = sys::c::CloseHandle(self.0); + } } } } @@ -333,7 +351,11 @@ impl HandleOrNull { #[stable(feature = "io_safety", since = "1.63.0")] #[inline] pub unsafe fn from_raw_handle(handle: RawHandle) -> Self { - Self(OwnedHandle::from_raw_handle(handle)) + Self(handle) + } + + fn is_valid(&self) -> bool { + !self.0.is_null() } } @@ -356,7 +378,11 @@ impl HandleOrInvalid { #[stable(feature = "io_safety", since = "1.63.0")] #[inline] pub unsafe fn from_raw_handle(handle: RawHandle) -> Self { - Self(OwnedHandle::from_raw_handle(handle)) + Self(handle) + } + + fn is_valid(&self) -> bool { + self.0 != sys::c::INVALID_HANDLE_VALUE } } From 2005c2e54dcb575b1d308ad711266ba1d5102549 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Mar 2024 18:13:50 +0100 Subject: [PATCH 17/79] interpret: ensure that Place is never used for a different frame --- .../src/interpret/eval_context.rs | 14 +++-- .../rustc_const_eval/src/interpret/operand.rs | 5 +- .../rustc_const_eval/src/interpret/place.rs | 59 +++++++++++-------- compiler/rustc_const_eval/src/lib.rs | 1 + 4 files changed, 46 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index a484fbd892c6d..d98d210710253 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,4 +1,5 @@ use std::cell::Cell; +use std::ptr; use std::{fmt, mem}; use either::{Either, Left, Right}; @@ -279,6 +280,11 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { } }) } + + #[inline(always)] + pub(super) fn locals_addr(&self) -> usize { + ptr::addr_of!(self.locals).addr() + } } // FIXME: only used by miri, should be removed once translatable. @@ -1212,18 +1218,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.place { - Place::Local { frame, local, offset } => { + Place::Local { local, offset, locals_addr } => { + debug_assert_eq!(locals_addr, self.ecx.frame().locals_addr()); let mut allocs = Vec::new(); write!(fmt, "{local:?}")?; if let Some(offset) = offset { write!(fmt, "+{:#x}", offset.bytes())?; } - if frame != self.ecx.frame_idx() { - write!(fmt, " ({} frames up)", self.ecx.frame_idx() - frame)?; - } write!(fmt, ":")?; - match self.ecx.stack()[frame].locals[local].value { + match self.ecx.frame().locals[local].value { LocalValue::Dead => write!(fmt, " is dead")?, LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => { write!(fmt, " is uninitialized")? diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 317e5673b51bb..48b0dad47ff3e 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -661,9 +661,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { match place.as_mplace_or_local() { Left(mplace) => Ok(mplace.into()), - Right((frame, local, offset)) => { + Right((local, offset, locals_addr)) => { debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`. - let base = self.local_to_op(&self.stack()[frame], local, None)?; + debug_assert_eq!(locals_addr, self.frame().locals_addr()); + let base = self.local_to_op(&self.frame(), local, None)?; Ok(match offset { Some(offset) => base.offset(offset, place.layout, self)?, None => { diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 60f7710c11d7d..52a74f4340531 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -187,11 +187,13 @@ pub(super) enum Place { /// where in the local this place is located; if it is `None`, no projection has been applied. /// Such projections are meaningful even if the offset is 0, since they can change layouts. /// (Without that optimization, we'd just always be a `MemPlace`.) - /// Note that this only stores the frame index, not the thread this frame belongs to -- that is - /// implicit. This means a `Place` must never be moved across interpreter thread boundaries! + /// `Local` places always refer to the current stack frame, so they are unstable under + /// function calls/returns and switching betweens stacks of different threads! + /// We carry around the address of the `locals` buffer of the correct stack frame as a sanity + /// chec to be able to catch some cases of using a dangling `Place`. /// /// This variant shall not be used for unsized types -- those must always live in memory. - Local { frame: usize, local: mir::Local, offset: Option }, + Local { local: mir::Local, offset: Option, locals_addr: usize }, } /// An evaluated place, together with its type. @@ -233,10 +235,10 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> { #[inline(always)] pub fn as_mplace_or_local( &self, - ) -> Either, (usize, mir::Local, Option)> { + ) -> Either, (mir::Local, Option, usize)> { match self.place { Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout }), - Place::Local { frame, local, offset } => Right((frame, local, offset)), + Place::Local { local, offset, locals_addr } => Right((local, offset, locals_addr)), } } @@ -279,7 +281,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { ) -> InterpResult<'tcx, Self> { Ok(match self.as_mplace_or_local() { Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(), - Right((frame, local, old_offset)) => { + Right((local, old_offset, locals_addr)) => { debug_assert!(layout.is_sized(), "unsized locals should live in memory"); assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway... // `Place::Local` are always in-bounds of their surrounding local, so we can just @@ -292,7 +294,10 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> { .offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?, ); - PlaceTy { place: Place::Local { frame, local, offset: Some(new_offset) }, layout } + PlaceTy { + place: Place::Local { local, offset: Some(new_offset), locals_addr }, + layout, + } } }) } @@ -331,7 +336,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> { pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { fn as_mplace_or_local( &self, - ) -> Either, (usize, mir::Local, Option, TyAndLayout<'tcx>)>; + ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)>; fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, @@ -343,9 +348,9 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> { #[inline(always)] fn as_mplace_or_local( &self, - ) -> Either, (usize, mir::Local, Option, TyAndLayout<'tcx>)> { + ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)> { self.as_mplace_or_local() - .map_right(|(frame, local, offset)| (frame, local, offset, self.layout)) + .map_right(|(local, offset, locals_addr)| (local, offset, locals_addr, self.layout)) } #[inline(always)] @@ -361,7 +366,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { #[inline(always)] fn as_mplace_or_local( &self, - ) -> Either, (usize, mir::Local, Option, TyAndLayout<'tcx>)> { + ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)> { Left(self.clone()) } @@ -512,7 +517,7 @@ where let layout = self.layout_of_local(frame_ref, local, None)?; let place = if layout.is_sized() { // We can just always use the `Local` for sized values. - Place::Local { frame, local, offset: None } + Place::Local { local, offset: None, locals_addr: frame_ref.locals_addr() } } else { // Unsized `Local` isn't okay (we cannot store the metadata). match frame_ref.locals[local].access()? { @@ -611,15 +616,16 @@ where // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, // but not factored as a separate function. let mplace = match dest.as_mplace_or_local() { - Right((frame, local, offset, layout)) => { + Right((local, offset, locals_addr, layout)) => { if offset.is_some() { // This has been projected to a part of this local. We could have complicated // logic to still keep this local as an `Operand`... but it's much easier to // just fall back to the indirect path. dest.force_mplace(self)? } else { - M::before_access_local_mut(self, frame, local)?; - match self.stack_mut()[frame].locals[local].access_mut()? { + debug_assert_eq!(locals_addr, self.frame().locals_addr()); + M::before_access_local_mut(self, self.frame_idx(), local)?; + match self.frame_mut().locals[local].access_mut()? { Operand::Immediate(local_val) => { // Local can be updated in-place. *local_val = src; @@ -627,7 +633,7 @@ where // (*After* doing the update for borrow checker reasons.) if cfg!(debug_assertions) { let local_layout = - self.layout_of_local(&self.stack()[frame], local, None)?; + self.layout_of_local(&self.frame(), local, None)?; match (src, local_layout.abi) { (Immediate::Scalar(scalar), Abi::Scalar(s)) => { assert_eq!(scalar.size(), s.size(self)) @@ -725,7 +731,7 @@ where ) -> InterpResult<'tcx> { let mplace = match dest.as_mplace_or_local() { Left(mplace) => mplace, - Right((frame, local, offset, layout)) => { + Right((local, offset, locals_addr, layout)) => { if offset.is_some() { // This has been projected to a part of this local. We could have complicated // logic to still keep this local as an `Operand`... but it's much easier to @@ -733,8 +739,9 @@ where // FIXME: share the logic with `write_immediate_no_validate`. dest.force_mplace(self)? } else { - M::before_access_local_mut(self, frame, local)?; - match self.stack_mut()[frame].locals[local].access_mut()? { + debug_assert_eq!(locals_addr, self.frame().locals_addr()); + M::before_access_local_mut(self, self.frame_idx(), local)?; + match self.frame_mut().locals[local].access_mut()? { Operand::Immediate(local) => { *local = Immediate::Uninit; return Ok(()); @@ -912,17 +919,17 @@ where place: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let mplace = match place.place { - Place::Local { frame, local, offset } => { - M::before_access_local_mut(self, frame, local)?; - let whole_local = match self.stack_mut()[frame].locals[local].access_mut()? { + Place::Local { local, offset, locals_addr } => { + debug_assert_eq!(locals_addr, self.frame().locals_addr()); + M::before_access_local_mut(self, self.frame_idx(), local)?; + let whole_local = match self.frame_mut().locals[local].access_mut()? { &mut Operand::Immediate(local_val) => { // We need to make an allocation. // We need the layout of the local. We can NOT use the layout we got, // that might e.g., be an inner field of a struct with `Scalar` layout, // that has different alignment than the outer field. - let local_layout = - self.layout_of_local(&self.stack()[frame], local, None)?; + let local_layout = self.layout_of_local(&self.frame(), local, None)?; assert!(local_layout.is_sized(), "unsized locals cannot be immediate"); let mplace = self.allocate(local_layout, MemoryKind::Stack)?; // Preserve old value. (As an optimization, we can skip this if it was uninit.) @@ -936,11 +943,11 @@ where mplace.mplace, )?; } - M::after_local_allocated(self, frame, local, &mplace)?; + M::after_local_allocated(self, self.frame_idx(), local, &mplace)?; // Now we can call `access_mut` again, asserting it goes well, and actually // overwrite things. This points to the entire allocation, not just the part // the place refers to, i.e. we do this before we apply `offset`. - *self.stack_mut()[frame].locals[local].access_mut().unwrap() = + *self.frame_mut().locals[local].access_mut().unwrap() = Operand::Indirect(mplace.mplace); mplace.mplace } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 51836063945e4..1e7ee208af1ab 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -14,6 +14,7 @@ Rust MIR: a lowered representation of Rust. #![feature(generic_nonzero)] #![feature(let_chains)] #![feature(slice_ptr_get)] +#![feature(strict_provenance)] #![feature(never_type)] #![feature(trait_alias)] #![feature(try_blocks)] From b888e895dec646657d6444652615a69c87276e5a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Mar 2024 18:15:56 +0100 Subject: [PATCH 18/79] remove a machine hook that is no longer used --- .../src/interpret/eval_context.rs | 3 --- .../rustc_const_eval/src/interpret/machine.rs | 18 ------------------ .../rustc_const_eval/src/interpret/place.rs | 3 --- 3 files changed, 24 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index d98d210710253..f3ebc3f51d899 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -221,9 +221,6 @@ impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> { /// Overwrite the local. If the local can be overwritten in place, return a reference /// to do so; otherwise return the `MemPlace` to consult instead. - /// - /// Note: Before calling this, call the `before_access_local_mut` machine hook! You may be - /// invalidating machine invariants otherwise! #[inline(always)] pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand> { match &mut self.value { diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 90a654a12294b..5e6862ad47506 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -260,24 +260,6 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { F2::NAN } - /// Called before writing the specified `local` of the `frame`. - /// Since writing a ZST is not actually accessing memory or locals, this is never invoked - /// for ZST reads. - /// - /// Due to borrow checker trouble, we indicate the `frame` as an index rather than an `&mut - /// Frame`. - #[inline(always)] - fn before_access_local_mut<'a>( - _ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - _frame: usize, - _local: mir::Local, - ) -> InterpResult<'tcx> - where - 'tcx: 'mir, - { - Ok(()) - } - /// Called before a basic block terminator is executed. #[inline] fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 52a74f4340531..7bd4463406698 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -624,7 +624,6 @@ where dest.force_mplace(self)? } else { debug_assert_eq!(locals_addr, self.frame().locals_addr()); - M::before_access_local_mut(self, self.frame_idx(), local)?; match self.frame_mut().locals[local].access_mut()? { Operand::Immediate(local_val) => { // Local can be updated in-place. @@ -740,7 +739,6 @@ where dest.force_mplace(self)? } else { debug_assert_eq!(locals_addr, self.frame().locals_addr()); - M::before_access_local_mut(self, self.frame_idx(), local)?; match self.frame_mut().locals[local].access_mut()? { Operand::Immediate(local) => { *local = Immediate::Uninit; @@ -921,7 +919,6 @@ where let mplace = match place.place { Place::Local { local, offset, locals_addr } => { debug_assert_eq!(locals_addr, self.frame().locals_addr()); - M::before_access_local_mut(self, self.frame_idx(), local)?; let whole_local = match self.frame_mut().locals[local].access_mut()? { &mut Operand::Immediate(local_val) => { // We need to make an allocation. From 4497990dff8ce181fa12757eb4e786b85025ae61 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Mar 2024 18:23:26 +0100 Subject: [PATCH 19/79] remove some frame parameters that are no longer needed --- .../rustc_const_eval/src/interpret/eval_context.rs | 4 ++-- compiler/rustc_const_eval/src/interpret/operand.rs | 14 +++++++------- compiler/rustc_const_eval/src/interpret/place.rs | 12 ++++++------ .../rustc_const_eval/src/interpret/projection.rs | 2 +- .../rustc_const_eval/src/interpret/terminator.rs | 2 +- src/tools/miri/src/helpers.rs | 2 +- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index f3ebc3f51d899..50e481030a322 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -648,7 +648,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } #[inline(always)] - pub fn layout_of_local( + pub(super) fn layout_of_local( &self, frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, @@ -899,7 +899,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // Copy return value. Must of course happen *before* we deallocate the locals. let copy_ret_result = if !unwinding { let op = self - .local_to_op(self.frame(), mir::RETURN_PLACE, None) + .local_to_op(mir::RETURN_PLACE, None) .expect("return place should always be live"); let dest = self.frame().return_place.clone(); let err = if self.stack().len() == 1 { diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 48b0dad47ff3e..e49067a2f0063 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -13,9 +13,9 @@ use rustc_middle::{mir, ty}; use rustc_target::abi::{self, Abi, HasDataLayout, Size}; use super::{ - alloc_range, from_known_layout, mir_assign_valid_types, CtfeProvenance, Frame, InterpCx, - InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, - Projectable, Provenance, Scalar, + alloc_range, from_known_layout, mir_assign_valid_types, CtfeProvenance, InterpCx, InterpResult, + MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, Projectable, + Provenance, Scalar, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -633,17 +633,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - /// Read from a local. + /// Read from a local of the current frame. /// Will not access memory, instead an indirect `Operand` is returned. /// /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an /// OpTy from a local. pub fn local_to_op( &self, - frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + let frame = self.frame(); let layout = self.layout_of_local(frame, local, layout)?; let op = *frame.locals[local].access()?; if matches!(op, Operand::Immediate(_)) { @@ -664,7 +664,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Right((local, offset, locals_addr)) => { debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`. debug_assert_eq!(locals_addr, self.frame().locals_addr()); - let base = self.local_to_op(&self.frame(), local, None)?; + let base = self.local_to_op(local, None)?; Ok(match offset { Some(offset) => base.offset(offset, place.layout, self)?, None => { @@ -688,7 +688,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // here is not the entire place. let layout = if mir_place.projection.is_empty() { layout } else { None }; - let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?; + let mut op = self.local_to_op(mir_place.local, layout)?; // Using `try_fold` turned out to be bad for performance, hence the loop. for elem in mir_place.projection.iter() { op = self.project(&op, elem)? diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 7bd4463406698..90ded6c09db12 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -506,21 +506,21 @@ where Ok((mplace, len)) } + /// Turn a local in the current frame into a place. pub fn local_to_place( &self, - frame: usize, local: mir::Local, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { // Other parts of the system rely on `Place::Local` never being unsized. // So we eagerly check here if this local has an MPlace, and if yes we use it. - let frame_ref = &self.stack()[frame]; - let layout = self.layout_of_local(frame_ref, local, None)?; + let frame = self.frame(); + let layout = self.layout_of_local(frame, local, None)?; let place = if layout.is_sized() { // We can just always use the `Local` for sized values. - Place::Local { local, offset: None, locals_addr: frame_ref.locals_addr() } + Place::Local { local, offset: None, locals_addr: frame.locals_addr() } } else { // Unsized `Local` isn't okay (we cannot store the metadata). - match frame_ref.locals[local].access()? { + match frame.locals[local].access()? { Operand::Immediate(_) => bug!(), Operand::Indirect(mplace) => Place::Ptr(*mplace), } @@ -535,7 +535,7 @@ where &self, mir_place: mir::Place<'tcx>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?; + let mut place = self.local_to_place(mir_place.local)?; // Using `try_fold` turned out to be bad for performance, hence the loop. for elem in mir_place.projection.iter() { place = self.project(&place, elem)? diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 7b68a37fdf3ea..5ff78f7b8c90b 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -357,7 +357,7 @@ where Deref => self.deref_pointer(&base.to_op(self)?)?.into(), Index(local) => { let layout = self.layout_of(self.tcx.types.usize)?; - let n = self.local_to_op(self.frame(), local, Some(layout))?; + let n = self.local_to_op(local, Some(layout))?; let n = self.read_target_usize(&n)?; self.project_index(base, n)? } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index bafb8cb0018c2..82fb7ff1840f0 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -631,7 +631,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { body.args_iter() .map(|local| ( local, - self.layout_of_local(self.frame(), local, None).unwrap().ty + self.layout_of_local(self.frame(), local, None).unwrap().ty, )) .collect::>() ); diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 9e4b5fe8ad780..ba9239085ce7b 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -411,7 +411,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { .ok_or_else(|| err_ub_format!("callee has fewer arguments than expected"))?; // Make the local live, and insert the initial value. this.storage_live(local)?; - let callee_arg = this.local_to_place(this.frame_idx(), local)?; + let callee_arg = this.local_to_place(local)?; this.write_immediate(*arg, &callee_arg)?; } if callee_args.next().is_some() { From bcc3f193b87ee9f843be68c138af830d75ccb1dd Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 9 Mar 2024 10:15:57 -0700 Subject: [PATCH 20/79] rustdoc-search: depth limit `T` -> `U` unboxing Profiler output: https://notriddle.com/rustdoc-html-demo-9/search-unbox-limit/ This is a performance enhancement aimed at a problem I found while using type-driven search on the Rust compiler. It is caused by [`Interner`], a trait with 41 associated types, many of which recurse back to `Self` again. This caused search.js to struggle. It eventually terminates, after about 10 minutes of turning my PC into a space header, but it's doing `41!` unifications and that's too slow. [`Interner`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait.Interner.html --- src/librustdoc/html/static/js/search.js | 139 ++++++++++++++++++------ 1 file changed, 108 insertions(+), 31 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 7995a33f09f9b..41fab540dc2d2 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1,3 +1,4 @@ +// ignore-tidy-filelength /* global addClass, getNakedUrl, getSettingValue */ /* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi, exports */ @@ -80,6 +81,13 @@ const longItemTypes = [ const TY_GENERIC = itemTypes.indexOf("generic"); const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; +// Hard limit on how deep to recurse into generics when doing type-driven search. +// This needs limited, partially because +// a search for `Ty` shouldn't match `WithInfcx>>>>`, +// but mostly because this is the simplest and most principled way to limit the number +// of permutations we need to check. +const UNBOXING_LIMIT = 5; + // In the search display, allows to switch between tabs. function printTab(nb) { let iter = 0; @@ -1383,10 +1391,23 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {Map|null} mgensIn * - Map functions generics to query generics (never modified). * @param {null|Map -> bool} solutionCb - Called for each `mgens` solution. + * @param {number} unboxingDepth + * - Limit checks that Ty matches Vec, + * but not Vec>>>> * * @return {boolean} - Returns true if a match, false otherwise. */ - function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) { + function unifyFunctionTypes( + fnTypesIn, + queryElems, + whereClause, + mgensIn, + solutionCb, + unboxingDepth + ) { + if (unboxingDepth >= UNBOXING_LIMIT) { + return false; + } /** * @type Map|null */ @@ -1405,7 +1426,7 @@ if (parserState.userQuery[parserState.pos] === "[") { && queryElems[0].bindings.size === 0) { const queryElem = queryElems[0]; for (const fnType of fnTypesIn) { - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) { + if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { continue; } if (fnType.id < 0 && queryElem.id < 0) { @@ -1424,7 +1445,13 @@ if (parserState.userQuery[parserState.pos] === "[") { } } for (const fnType of fnTypesIn) { - if (!unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) { + if (!unifyFunctionTypeIsUnboxCandidate( + fnType, + queryElem, + whereClause, + mgens, + unboxingDepth + 1 + )) { continue; } if (fnType.id < 0) { @@ -1439,7 +1466,8 @@ if (parserState.userQuery[parserState.pos] === "[") { queryElems, whereClause, mgensScratch, - solutionCb + solutionCb, + unboxingDepth + 1 )) { return true; } @@ -1448,7 +1476,8 @@ if (parserState.userQuery[parserState.pos] === "[") { queryElems, whereClause, mgens ? new Map(mgens) : null, - solutionCb + solutionCb, + unboxingDepth + 1 )) { return true; } @@ -1484,7 +1513,7 @@ if (parserState.userQuery[parserState.pos] === "[") { let queryElemsTmp = null; for (let i = flast; i >= 0; i -= 1) { const fnType = fnTypes[i]; - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) { + if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { continue; } let mgensScratch; @@ -1521,7 +1550,8 @@ if (parserState.userQuery[parserState.pos] === "[") { fnType, queryElem, whereClause, - mgensScratch + mgensScratch, + unboxingDepth ); if (!solution) { return false; @@ -1533,14 +1563,16 @@ if (parserState.userQuery[parserState.pos] === "[") { queryElem.generics, whereClause, simplifiedMgens, - solutionCb + solutionCb, + unboxingDepth ); if (passesUnification) { return true; } } return false; - } + }, + unboxingDepth ); if (passesUnification) { return true; @@ -1552,7 +1584,13 @@ if (parserState.userQuery[parserState.pos] === "[") { } for (let i = flast; i >= 0; i -= 1) { const fnType = fnTypes[i]; - if (!unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) { + if (!unifyFunctionTypeIsUnboxCandidate( + fnType, + queryElem, + whereClause, + mgens, + unboxingDepth + 1 + )) { continue; } let mgensScratch; @@ -1576,7 +1614,8 @@ if (parserState.userQuery[parserState.pos] === "[") { queryElems, whereClause, mgensScratch, - solutionCb + solutionCb, + unboxingDepth + 1 ); if (passesUnification) { return true; @@ -1595,11 +1634,10 @@ if (parserState.userQuery[parserState.pos] === "[") { * * @param {FunctionType} fnType * @param {QueryElement} queryElem - * @param {[FunctionSearchType]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn - Map functions generics to query generics. * @returns {boolean} */ - function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgensIn) { + function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgensIn) { // type filters look like `trait:Read` or `enum:Result` if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { return false; @@ -1694,9 +1732,16 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {[FunctionType]} whereClause - Trait bounds for generic items. * @param {Map} mgensIn - Map functions generics to query generics. * Never modified. + * @param {number} unboxingDepth * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}} */ - function unifyFunctionTypeCheckBindings(fnType, queryElem, whereClause, mgensIn) { + function unifyFunctionTypeCheckBindings( + fnType, + queryElem, + whereClause, + mgensIn, + unboxingDepth + ) { if (fnType.bindings.size < queryElem.bindings.size) { return false; } @@ -1723,7 +1768,8 @@ if (parserState.userQuery[parserState.pos] === "[") { // return `false` makes unifyFunctionTypes return the full set of // possible solutions return false; - } + }, + unboxingDepth ); return newSolutions; }); @@ -1753,9 +1799,19 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {QueryElement} queryElem * @param {[FunctionType]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map functions generics to query generics. + * @param {number} unboxingDepth * @returns {boolean} */ - function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) { + function unifyFunctionTypeIsUnboxCandidate( + fnType, + queryElem, + whereClause, + mgens, + unboxingDepth + ) { + if (unboxingDepth >= UNBOXING_LIMIT) { + return false; + } if (fnType.id < 0 && queryElem.id >= 0) { if (!whereClause) { return false; @@ -1777,14 +1833,21 @@ if (parserState.userQuery[parserState.pos] === "[") { whereClause[(-fnType.id) - 1], queryElem, whereClause, - mgensTmp + mgensTmp, + unboxingDepth ); } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { const simplifiedGenerics = [ ...fnType.generics, ...Array.from(fnType.bindings.values()).flat(), ]; - return checkIfInList(simplifiedGenerics, queryElem, whereClause, mgens); + return checkIfInList( + simplifiedGenerics, + queryElem, + whereClause, + mgens, + unboxingDepth + ); } return false; } @@ -1796,13 +1859,14 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {Array} list * @param {QueryElement} elem - The element from the parsed query. * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgens - Map functions generics to query generics. + * @param {Map|null} mgens - Map functions generics to query generics. + * @param {number} unboxingDepth * * @return {boolean} - Returns true if found, false otherwise. */ - function checkIfInList(list, elem, whereClause, mgens) { + function checkIfInList(list, elem, whereClause, mgens, unboxingDepth) { for (const entry of list) { - if (checkType(entry, elem, whereClause, mgens)) { + if (checkType(entry, elem, whereClause, mgens, unboxingDepth)) { return true; } } @@ -1816,14 +1880,23 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {Row} row * @param {QueryElement} elem - The element from the parsed query. * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgens - Map functions generics to query generics. + * @param {Map|null} mgens - Map functions generics to query generics. * * @return {boolean} - Returns true if the type matches, false otherwise. */ - function checkType(row, elem, whereClause, mgens) { + function checkType(row, elem, whereClause, mgens, unboxingDepth) { + if (unboxingDepth >= UNBOXING_LIMIT) { + return false; + } if (row.bindings.size === 0 && elem.bindings.size === 0) { - if (elem.id < 0) { - return row.id < 0 || checkIfInList(row.generics, elem, whereClause, mgens); + if (elem.id < 0 && mgens === null) { + return row.id < 0 || checkIfInList( + row.generics, + elem, + whereClause, + mgens, + unboxingDepth + 1 + ); } if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 && @@ -1834,11 +1907,12 @@ if (parserState.userQuery[parserState.pos] === "[") { row.generics, elem, whereClause, - mgens + mgens, + unboxingDepth ); } } - return unifyFunctionTypes([row], [elem], whereClause, mgens); + return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth); } /** @@ -2053,9 +2127,9 @@ if (parserState.userQuery[parserState.pos] === "[") { ); if (tfpDist !== null) { const in_args = row.type && row.type.inputs - && checkIfInList(row.type.inputs, elem, row.type.where_clause); + && checkIfInList(row.type.inputs, elem, row.type.where_clause, null, 0); const returned = row.type && row.type.output - && checkIfInList(row.type.output, elem, row.type.where_clause); + && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); if (in_args) { results_in_args.max_dist = Math.max(results_in_args.max_dist || 0, tfpDist); const maxDist = results_in_args.size < MAX_RESULTS ? @@ -2141,9 +2215,12 @@ if (parserState.userQuery[parserState.pos] === "[") { row.type.output, parsedQuery.returned, row.type.where_clause, - mgens + mgens, + null, + 0 // unboxing depth ); - } + }, + 0 // unboxing depth )) { return; } From c3342b41b52aa4f8a4497a696ee783952fca36da Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 9 Mar 2024 18:28:29 +0100 Subject: [PATCH 21/79] remove unnecessary frame parameter from after_local_allocated --- compiler/rustc_const_eval/src/interpret/eval_context.rs | 5 +++-- compiler/rustc_const_eval/src/interpret/machine.rs | 1 - compiler/rustc_const_eval/src/interpret/place.rs | 2 +- src/tools/miri/src/machine.rs | 3 +-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 50e481030a322..7526acf145436 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,5 +1,4 @@ use std::cell::Cell; -use std::ptr; use std::{fmt, mem}; use either::{Either, Left, Right}; @@ -278,9 +277,11 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { }) } + /// Returns the address of the buffer where the locals are stored. This is used by `Place` as a + /// sanity check to detect bugs where we mix up which stack frame a place refers to. #[inline(always)] pub(super) fn locals_addr(&self) -> usize { - ptr::addr_of!(self.locals).addr() + self.locals.raw.as_ptr().addr() } } diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 5e6862ad47506..04de516225065 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -508,7 +508,6 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized { #[inline(always)] fn after_local_allocated( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: usize, _local: mir::Local, _mplace: &MPlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 90ded6c09db12..1a2f1194f89a2 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -940,7 +940,7 @@ where mplace.mplace, )?; } - M::after_local_allocated(self, self.frame_idx(), local, &mplace)?; + M::after_local_allocated(self, local, &mplace)?; // Now we can call `access_mut` again, asserting it goes well, and actually // overwrite things. This points to the entire allocation, not just the part // the place refers to, i.e. we do this before we apply `offset`. diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 19d02c6f74620..20deeb22887f0 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1473,14 +1473,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { fn after_local_allocated( ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: usize, local: mir::Local, mplace: &MPlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else { panic!("after_local_allocated should only be called on fresh allocations"); }; - let local_decl = &ecx.active_thread_stack()[frame].body.local_decls[local]; + let local_decl = &ecx.frame().body.local_decls[local]; let span = local_decl.source_info.span; ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None)); Ok(()) From 39db6a097211f01fd6a274e1dea9936932462f56 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 10 Mar 2024 12:28:23 +0100 Subject: [PATCH 22/79] add test ensuring simd codegen checks don't run when a static assertion failed --- tests/ui/simd/const-err-trumps-simd-err.rs | 24 +++++++++++++++++++ .../ui/simd/const-err-trumps-simd-err.stderr | 17 +++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/ui/simd/const-err-trumps-simd-err.rs create mode 100644 tests/ui/simd/const-err-trumps-simd-err.stderr diff --git a/tests/ui/simd/const-err-trumps-simd-err.rs b/tests/ui/simd/const-err-trumps-simd-err.rs new file mode 100644 index 0000000000000..06a747273ab7f --- /dev/null +++ b/tests/ui/simd/const-err-trumps-simd-err.rs @@ -0,0 +1,24 @@ +//@build-fail +//! Make sure that monomorphization-time const errors from `static_assert` take priority over the +//! error from simd_extract. Basically this checks that if a const fails to evaluate in some +//! function, we don't bother codegen'ing the function. +#![feature(generic_arg_infer)] +#![feature(core_intrinsics)] +#![feature(repr_simd)] +#![feature(inline_const)] +use std::intrinsics::simd::*; + +#[repr(simd)] +#[allow(non_camel_case_types)] +struct int8x4_t(u8,u8,u8,u8); + +fn get_elem(a: int8x4_t) -> u8 { + const { assert!(LANE < 4); } // the error should be here... + //~^ ERROR failed + //~| assertion failed + unsafe { simd_extract(a, LANE) } // ...not here +} + +fn main() { + get_elem::<4>(int8x4_t(0,0,0,0)); +} diff --git a/tests/ui/simd/const-err-trumps-simd-err.stderr b/tests/ui/simd/const-err-trumps-simd-err.stderr new file mode 100644 index 0000000000000..6e6aba8b6f186 --- /dev/null +++ b/tests/ui/simd/const-err-trumps-simd-err.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `get_elem::<4>::{constant#0}` failed + --> $DIR/const-err-trumps-simd-err.rs:16:13 + | +LL | const { assert!(LANE < 4); } // the error should be here... + | ^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: LANE < 4', $DIR/const-err-trumps-simd-err.rs:16:13 + | + = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn get_elem::<4>` + --> $DIR/const-err-trumps-simd-err.rs:23:5 + | +LL | get_elem::<4>(int8x4_t(0,0,0,0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. From d765fb8fafba4f6a25e34c7568d7bc8e3be8226d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 10 Mar 2024 12:39:53 +0100 Subject: [PATCH 23/79] add comments explaining where post-mono const eval errors abort compilation --- compiler/rustc_codegen_cranelift/src/constant.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/constant.rs | 6 +++--- compiler/rustc_codegen_ssa/src/mir/mod.rs | 3 ++- compiler/rustc_monomorphize/src/collector.rs | 9 ++++++--- compiler/rustc_monomorphize/src/partitioning.rs | 3 +++ 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 18c5960ffc68a..cec479218b71f 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -71,7 +71,7 @@ pub(crate) fn eval_mir_constant<'tcx>( // This cannot fail because we checked all required_consts in advance. let val = cv .eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(constant.span)) - .expect("erroneous constant not captured by required_consts"); + .expect("erroneous constant missed by mono item collection"); (val, cv.ty()) } diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index d532bd9042676..ff899c50b3de6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -21,11 +21,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue<'tcx> { - // `MirUsedCollector` visited all constants before codegen began, so if we got here there - // can be no more constants that fail to evaluate. + // `MirUsedCollector` visited all required_consts before codegen began, so if we got here + // there can be no more constants that fail to evaluate. self.monomorphize(constant.const_) .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span)) - .expect("erroneous constant not captured by required_consts") + .expect("erroneous constant missed by mono item collection") } /// This is a convenience helper for `simd_shuffle_indices`. It has the precondition diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index a6fcf1fd38c1f..89fd57b6d96ed 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -211,7 +211,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // It may seem like we should iterate over `required_consts` to ensure they all successfully // evaluate; however, the `MirUsedCollector` already did that during the collection phase of - // monomorphization so we don't have to do it again. + // monomorphization, and if there is an error during collection then codegen never starts -- so + // we don't have to do it again. fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 32f823a5ac68f..573a8f68347ec 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -814,13 +814,16 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { self.super_rvalue(rvalue, location); } - /// This does not walk the constant, as it has been handled entirely here and trying - /// to walk it would attempt to evaluate the `ty::Const` inside, which doesn't necessarily - /// work, as some constants cannot be represented in the type system. + /// This does not walk the MIR of the constant as that is not needed for codegen, all we need is + /// to ensure that the constant evaluates successfully and walk the result. #[instrument(skip(self), level = "debug")] fn visit_constant(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) { let const_ = self.monomorphize(constant.const_); let param_env = ty::ParamEnv::reveal_all(); + // Evaluate the constant. This makes const eval failure a collection-time error (rather than + // a codegen-time error). rustc stops after collection if there was an error, so this + // ensures codegen never has to worry about failing consts. + // (codegen relies on this and ICEs will happen if this is violated.) let val = match const_.eval(self.tcx, param_env, None) { Ok(v) => v, Err(ErrorHandled::Reported(..)) => return, diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 8bebc30e4356a..c9ebc73246048 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1114,6 +1114,9 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_mode); + // If there was an error during collection (e.g. from one of the constants we evaluated), + // then we stop here. This way codegen does not have to worry about failing constants. + // (codegen relies on this and ICEs will happen if this is violated.) tcx.dcx().abort_if_errors(); let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || { From 15b71f491f39c0d2e3b733c13328f3b513900309 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sun, 13 Nov 2022 04:09:20 -0500 Subject: [PATCH 24/79] Add CStr::bytes iterator --- library/core/src/ffi/c_str.rs | 89 +++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 111fb83088b82..1dafc8c1f868c 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -5,8 +5,11 @@ use crate::error::Error; use crate::ffi::c_char; use crate::fmt; use crate::intrinsics; +use crate::iter::FusedIterator; +use crate::marker::PhantomData; use crate::ops; use crate::ptr::addr_of; +use crate::ptr::NonNull; use crate::slice; use crate::slice::memchr; use crate::str; @@ -617,6 +620,26 @@ impl CStr { unsafe { &*(addr_of!(self.inner) as *const [u8]) } } + /// Iterates over the bytes in this C string. + /// + /// The returned iterator will **not** contain the trailing nul terminator + /// that this C string has. + /// + /// # Examples + /// + /// ``` + /// #![feature(cstr_bytes)] + /// use std::ffi::CStr; + /// + /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); + /// assert!(cstr.bytes().eq(*b"foo")); + /// ``` + #[inline] + #[unstable(feature = "cstr_bytes", issue = "112115")] + pub fn bytes(&self) -> Bytes<'_> { + Bytes::new(self) + } + /// Yields a &[str] slice if the `CStr` contains valid UTF-8. /// /// If the contents of the `CStr` are valid UTF-8 data, this @@ -735,3 +758,69 @@ const unsafe fn const_strlen(ptr: *const c_char) -> usize { intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt) } } + +/// An iterator over the bytes of a [`CStr`], without the nul terminator. +/// +/// This struct is created by the [`bytes`] method on [`CStr`]. +/// See its documentation for more. +/// +/// [`bytes`]: CStr::bytes +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "cstr_bytes", issue = "112115")] +#[derive(Clone, Debug)] +pub struct Bytes<'a> { + // since we know the string is nul-terminated, we only need one pointer + ptr: NonNull, + phantom: PhantomData<&'a u8>, +} +impl<'a> Bytes<'a> { + #[inline] + fn new(s: &'a CStr) -> Self { + Self { + // SAFETY: Because we have a valid reference to the string, we know + // that its pointer is non-null. + ptr: unsafe { NonNull::new_unchecked(s.as_ptr() as *const u8 as *mut u8) }, + phantom: PhantomData, + } + } + + #[inline] + fn is_empty(&self) -> bool { + // SAFETY: We uphold that the pointer is always valid to dereference + // by starting with a valid C string and then never incrementing beyond + // the nul terminator. + unsafe { *self.ptr.as_ref() == 0 } + } +} + +#[unstable(feature = "cstr_bytes", issue = "112115")] +impl Iterator for Bytes<'_> { + type Item = u8; + + #[inline] + fn next(&mut self) -> Option { + // SAFETY: We only choose a pointer from a valid C string, which must + // be non-null and contain at least one value. Since we always stop at + // the nul terminator, which is guaranteed to exist, we can assume that + // the pointer is non-null and valid. This lets us safely dereference + // it and assume that adding 1 will create a new, non-null, valid + // pointer. + unsafe { + let ret = *self.ptr.as_ref(); + if ret == 0 { + None + } else { + self.ptr = NonNull::new_unchecked(self.ptr.as_ptr().offset(1)); + Some(ret) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.is_empty() { (0, Some(0)) } else { (1, None) } + } +} + +#[unstable(feature = "cstr_bytes", issue = "112115")] +impl FusedIterator for Bytes<'_> {} From 207fe38630fdcfecea048aa41c6a9dd47c19573f Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Fri, 8 Mar 2024 17:11:27 -0500 Subject: [PATCH 25/79] copy byval argument to alloca if alignment is insufficient --- compiler/rustc_codegen_llvm/src/abi.rs | 104 ++++++++------- compiler/rustc_codegen_ssa/src/mir/mod.rs | 62 +++++---- .../codegen/align-byval-alignment-mismatch.rs | 126 ++++++++++++++++++ 3 files changed, 220 insertions(+), 72 deletions(-) create mode 100644 tests/codegen/align-byval-alignment-mismatch.rs diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 147939d3a529a..e5f5146fac8fb 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -203,57 +203,63 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { val: &'ll Value, dst: PlaceRef<'tcx, &'ll Value>, ) { - if self.is_ignore() { - return; - } - if self.is_sized_indirect() { - OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst) - } else if self.is_unsized_indirect() { - bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); - } else if let PassMode::Cast { cast, pad_i32: _ } = &self.mode { - // FIXME(eddyb): Figure out when the simpler Store is safe, clang - // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. - let can_store_through_cast_ptr = false; - if can_store_through_cast_ptr { - bx.store(val, dst.llval, self.layout.align.abi); - } else { - // The actual return type is a struct, but the ABI - // adaptation code has cast it into some scalar type. The - // code that follows is the only reliable way I have - // found to do a transform like i64 -> {i32,i32}. - // Basically we dump the data onto the stack then memcpy it. - // - // Other approaches I tried: - // - Casting rust ret pointer to the foreign type and using Store - // is (a) unsafe if size of foreign type > size of rust type and - // (b) runs afoul of strict aliasing rules, yielding invalid - // assembly under -O (specifically, the store gets removed). - // - Truncating foreign type to correct integral type and then - // bitcasting to the struct type yields invalid cast errors. - - // We instead thus allocate some scratch space... - let scratch_size = cast.size(bx); - let scratch_align = cast.align(bx); - let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align); - bx.lifetime_start(llscratch, scratch_size); - - // ... where we first store the value... - bx.store(val, llscratch, scratch_align); - - // ... and then memcpy it to the intended destination. - bx.memcpy( - dst.llval, - self.layout.align.abi, - llscratch, - scratch_align, - bx.const_usize(self.layout.size.bytes()), - MemFlags::empty(), - ); + match &self.mode { + PassMode::Ignore => {} + // Sized indirect arguments + PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + let align = attrs.pointee_align.unwrap_or(self.layout.align.abi); + OperandValue::Ref(val, None, align).store(bx, dst); + } + // Unsized indirect qrguments + PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); + } + PassMode::Cast { cast, pad_i32: _ } => { + // FIXME(eddyb): Figure out when the simpler Store is safe, clang + // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. + let can_store_through_cast_ptr = false; + if can_store_through_cast_ptr { + bx.store(val, dst.llval, self.layout.align.abi); + } else { + // The actual return type is a struct, but the ABI + // adaptation code has cast it into some scalar type. The + // code that follows is the only reliable way I have + // found to do a transform like i64 -> {i32,i32}. + // Basically we dump the data onto the stack then memcpy it. + // + // Other approaches I tried: + // - Casting rust ret pointer to the foreign type and using Store + // is (a) unsafe if size of foreign type > size of rust type and + // (b) runs afoul of strict aliasing rules, yielding invalid + // assembly under -O (specifically, the store gets removed). + // - Truncating foreign type to correct integral type and then + // bitcasting to the struct type yields invalid cast errors. + + // We instead thus allocate some scratch space... + let scratch_size = cast.size(bx); + let scratch_align = cast.align(bx); + let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align); + bx.lifetime_start(llscratch, scratch_size); + + // ... where we first store the value... + bx.store(val, llscratch, scratch_align); + + // ... and then memcpy it to the intended destination. + bx.memcpy( + dst.llval, + self.layout.align.abi, + llscratch, + scratch_align, + bx.const_usize(self.layout.size.bytes()), + MemFlags::empty(), + ); - bx.lifetime_end(llscratch, scratch_size); + bx.lifetime_end(llscratch, scratch_size); + } + } + _ => { + OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst); } - } else { - OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index a6fcf1fd38c1f..2ae51d18c7ef9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -367,29 +367,45 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } - if arg.is_sized_indirect() { - // Don't copy an indirect argument to an alloca, the caller - // already put it in a temporary alloca and gave it up. - // FIXME: lifetimes - let llarg = bx.get_param(llarg_idx); - llarg_idx += 1; - LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) - } else if arg.is_unsized_indirect() { - // As the storage for the indirect argument lives during - // the whole function call, we just copy the fat pointer. - let llarg = bx.get_param(llarg_idx); - llarg_idx += 1; - let llextra = bx.get_param(llarg_idx); - llarg_idx += 1; - let indirect_operand = OperandValue::Pair(llarg, llextra); - - let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout); - indirect_operand.store(bx, tmp); - LocalRef::UnsizedPlace(tmp) - } else { - let tmp = PlaceRef::alloca(bx, arg.layout); - bx.store_fn_arg(arg, &mut llarg_idx, tmp); - LocalRef::Place(tmp) + match arg.mode { + // Sized indirect arguments + PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + // Don't copy an indirect argument to an alloca, the caller already put it + // in a temporary alloca and gave it up. + // FIXME: lifetimes + if let Some(pointee_align) = attrs.pointee_align + && pointee_align < arg.layout.align.abi + { + // ...unless the argument is underaligned, then we need to copy it to + // a higher-aligned alloca. + let tmp = PlaceRef::alloca(bx, arg.layout); + bx.store_fn_arg(arg, &mut llarg_idx, tmp); + LocalRef::Place(tmp) + } else { + let llarg = bx.get_param(llarg_idx); + llarg_idx += 1; + LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) + } + } + // Unsized indirect qrguments + PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + // As the storage for the indirect argument lives during + // the whole function call, we just copy the fat pointer. + let llarg = bx.get_param(llarg_idx); + llarg_idx += 1; + let llextra = bx.get_param(llarg_idx); + llarg_idx += 1; + let indirect_operand = OperandValue::Pair(llarg, llextra); + + let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout); + indirect_operand.store(bx, tmp); + LocalRef::UnsizedPlace(tmp) + } + _ => { + let tmp = PlaceRef::alloca(bx, arg.layout); + bx.store_fn_arg(arg, &mut llarg_idx, tmp); + LocalRef::Place(tmp) + } } }) .collect::>(); diff --git a/tests/codegen/align-byval-alignment-mismatch.rs b/tests/codegen/align-byval-alignment-mismatch.rs new file mode 100644 index 0000000000000..306e3ce1358af --- /dev/null +++ b/tests/codegen/align-byval-alignment-mismatch.rs @@ -0,0 +1,126 @@ +// ignore-tidy-linelength +//@ revisions:i686-linux x86_64-linux + +//@[i686-linux] compile-flags: --target i686-unknown-linux-gnu +//@[i686-linux] needs-llvm-components: x86 +//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64-linux] needs-llvm-components: x86 + +// Tests that we correctly copy arguments into allocas when the alignment of the byval argument +// is different from the alignment of the Rust type. + +// For the following test cases: +// All of the `*_decreases_alignment` functions should codegen to a direct call, since the +// alignment is already sufficient. +// All off the `*_increases_alignment` functions should copy the argument to an alloca +// on i686-unknown-linux-gnu, since the alignment needs to be increased, and should codegen +// to a direct call on x86_64-unknown-linux-gnu, where byval alignment matches Rust alignment. + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![allow(non_camel_case_types)] + +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} + +// This type has align 1 in Rust, but as a byval argument on i686-linux, it will have align 4. +#[repr(C)] +#[repr(packed)] +struct Align1 { + x: u128, + y: u128, + z: u128, +} + +// This type has align 16 in Rust, but as a byval argument on i686-linux, it will have align 4. +#[repr(C)] +#[repr(align(16))] +struct Align16 { + x: u128, + y: u128, + z: u128, +} + +extern "C" { + fn extern_c_align1(x: Align1); + fn extern_c_align16(x: Align16); +} + +// CHECK-LABEL: @rust_to_c_increases_alignment +#[no_mangle] +pub unsafe fn rust_to_c_increases_alignment(x: Align1) { + // i686-linux: start: + // i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca %Align1, align 4 + // i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 4 {{.*}}[[ALLOCA]], ptr {{.*}}align 1 {{.*}}%x + // i686-linux-NEXT: call void @extern_c_align1({{.+}} [[ALLOCA]]) + + // x86_64-linux: start: + // x86_64-linux-NEXT: call void @extern_c_align1 + extern_c_align1(x); +} + +// CHECK-LABEL: @rust_to_c_decreases_alignment +#[no_mangle] +pub unsafe fn rust_to_c_decreases_alignment(x: Align16) { + // CHECK: start: + // CHECK-NEXT: call void @extern_c_align16 + extern_c_align16(x); +} + +extern "Rust" { + fn extern_rust_align1(x: Align1); + fn extern_rust_align16(x: Align16); +} + +// CHECK-LABEL: @c_to_rust_decreases_alignment +#[no_mangle] +pub unsafe extern "C" fn c_to_rust_decreases_alignment(x: Align1) { + // CHECK: start: + // CHECK-NEXT: call void @extern_rust_align1 + extern_rust_align1(x); +} + +// CHECK-LABEL: @c_to_rust_increases_alignment +#[no_mangle] +pub unsafe extern "C" fn c_to_rust_increases_alignment(x: Align16) { + // i686-linux: start: + // i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca %Align16, align 16 + // i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 16 {{.*}}[[ALLOCA]], ptr {{.*}}align 4 {{.*}}%0 + // i686-linux-NEXT: call void @extern_rust_align16({{.+}} [[ALLOCA]]) + + // x86_64-linux: start: + // x86_64-linux-NEXT: call void @extern_rust_align16 + extern_rust_align16(x); +} + +extern "Rust" { + fn extern_rust_ref_align1(x: &Align1); + fn extern_rust_ref_align16(x: &Align16); +} + +// CHECK-LABEL: @c_to_rust_ref_decreases_alignment +#[no_mangle] +pub unsafe extern "C" fn c_to_rust_ref_decreases_alignment(x: Align1) { + // CHECK: start: + // CHECK-NEXT: call void @extern_rust_ref_align1 + extern_rust_ref_align1(&x); +} + +// CHECK-LABEL: @c_to_rust_ref_increases_alignment +#[no_mangle] +pub unsafe extern "C" fn c_to_rust_ref_increases_alignment(x: Align16) { + // i686-linux: start: + // i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca %Align16, align 16 + // i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 16 {{.*}}[[ALLOCA]], ptr {{.*}}align 4 {{.*}}%0 + // i686-linux-NEXT: call void @extern_rust_ref_align16({{.+}} [[ALLOCA]]) + + // x86_64-linux: start: + // x86_64-linux-NEXT: call void @extern_rust_ref_align16 + extern_rust_ref_align16(&x); +} From 818f13095ac79db831078c7a2b3dede90eecf86f Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Mon, 11 Mar 2024 09:39:43 -0400 Subject: [PATCH 26/79] update make_indirect_byval comment about missing fix (this PR is the fix) --- compiler/rustc_target/src/abi/call/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index cb587e28a6c99..486afc5f8f30a 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -633,10 +633,8 @@ impl<'a, Ty> ArgAbi<'a, Ty> { /// If the resulting alignment differs from the type's alignment, /// the argument will be copied to an alloca with sufficient alignment, /// either in the caller (if the type's alignment is lower than the byval alignment) - /// or in the callee† (if the type's alignment is higher than the byval alignment), + /// or in the callee (if the type's alignment is higher than the byval alignment), /// to ensure that Rust code never sees an underaligned pointer. - /// - /// † This is currently broken, see . pub fn make_indirect_byval(&mut self, byval_align: Option) { assert!(!self.layout.is_unsized(), "used byval ABI for unsized layout"); self.make_indirect(); From fa5b9f09235d73b5b7ff0b9e61ca3804b29d9514 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 11 Mar 2024 09:17:23 -0700 Subject: [PATCH 27/79] rustdoc-search: stress test for associated types --- tests/rustdoc-js/auxiliary/interner.rs | 245 ++++++++++++++++++ tests/rustdoc-js/looks-like-rustc-interner.js | 9 + tests/rustdoc-js/looks-like-rustc-interner.rs | 5 + 3 files changed, 259 insertions(+) create mode 100644 tests/rustdoc-js/auxiliary/interner.rs create mode 100644 tests/rustdoc-js/looks-like-rustc-interner.js create mode 100644 tests/rustdoc-js/looks-like-rustc-interner.rs diff --git a/tests/rustdoc-js/auxiliary/interner.rs b/tests/rustdoc-js/auxiliary/interner.rs new file mode 100644 index 0000000000000..c95029be9f0f4 --- /dev/null +++ b/tests/rustdoc-js/auxiliary/interner.rs @@ -0,0 +1,245 @@ +#![feature(associated_type_defaults)] + +use std::cmp::Ord; +use std::fmt::{Debug, Formatter}; +use std::hash::Hash; +use std::ops::ControlFlow; + +pub trait Interner: Sized { + type DefId: Copy + Debug + Hash + Ord; + type AdtDef: Copy + Debug + Hash + Ord; + type GenericArgs: Copy + + DebugWithInfcx + + Hash + + Ord + + IntoIterator; + type GenericArg: Copy + DebugWithInfcx + Hash + Ord; + type Term: Copy + Debug + Hash + Ord; + type Binder>: BoundVars + TypeSuperVisitable; + type BoundVars: IntoIterator; + type BoundVar; + type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator>; + type Ty: Copy + + DebugWithInfcx + + Hash + + Ord + + Into + + IntoKind> + + TypeSuperVisitable + + Flags + + Ty; + type Tys: Copy + Debug + Hash + Ord + IntoIterator; + type AliasTy: Copy + DebugWithInfcx + Hash + Ord; + type ParamTy: Copy + Debug + Hash + Ord; + type BoundTy: Copy + Debug + Hash + Ord; + type PlaceholderTy: Copy + Debug + Hash + Ord + PlaceholderLike; + type ErrorGuaranteed: Copy + Debug + Hash + Ord; + type BoundExistentialPredicates: Copy + DebugWithInfcx + Hash + Ord; + type PolyFnSig: Copy + DebugWithInfcx + Hash + Ord; + type AllocId: Copy + Debug + Hash + Ord; + type Const: Copy + + DebugWithInfcx + + Hash + + Ord + + Into + + IntoKind> + + ConstTy + + TypeSuperVisitable + + Flags + + Const; + type AliasConst: Copy + DebugWithInfcx + Hash + Ord; + type PlaceholderConst: Copy + Debug + Hash + Ord + PlaceholderLike; + type ParamConst: Copy + Debug + Hash + Ord; + type BoundConst: Copy + Debug + Hash + Ord; + type ValueConst: Copy + Debug + Hash + Ord; + type ExprConst: Copy + DebugWithInfcx + Hash + Ord; + type Region: Copy + + DebugWithInfcx + + Hash + + Ord + + Into + + IntoKind> + + Flags + + Region; + type EarlyParamRegion: Copy + Debug + Hash + Ord; + type LateParamRegion: Copy + Debug + Hash + Ord; + type BoundRegion: Copy + Debug + Hash + Ord; + type InferRegion: Copy + DebugWithInfcx + Hash + Ord; + type PlaceholderRegion: Copy + Debug + Hash + Ord + PlaceholderLike; + type Predicate: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; + type TraitPredicate: Copy + Debug + Hash + Eq; + type RegionOutlivesPredicate: Copy + Debug + Hash + Eq; + type TypeOutlivesPredicate: Copy + Debug + Hash + Eq; + type ProjectionPredicate: Copy + Debug + Hash + Eq; + type NormalizesTo: Copy + Debug + Hash + Eq; + type SubtypePredicate: Copy + Debug + Hash + Eq; + type CoercePredicate: Copy + Debug + Hash + Eq; + type ClosureKind: Copy + Debug + Hash + Eq; + + // Required method + fn mk_canonical_var_infos( + self, + infos: &[CanonicalVarInfo] + ) -> Self::CanonicalVars; +} + +pub trait DebugWithInfcx: Debug { + // Required method + fn fmt>( + this: WithInfcx<'_, Infcx, &Self>, + f: &mut Formatter<'_> + ) -> std::fmt::Result; +} + +pub trait TypeVisitable: Debug + Clone { + // Required method + fn visit_with>(&self, visitor: &mut V) -> V::Result; +} + +pub trait BoundVars { + // Required methods + fn bound_vars(&self) -> I::BoundVars; + fn has_no_bound_vars(&self) -> bool; +} + +pub trait TypeSuperVisitable: TypeVisitable { + // Required method + fn super_visit_with>(&self, visitor: &mut V) -> V::Result; +} + +pub struct CanonicalVarInfo { + pub kind: CanonicalVarKind, +} + +pub struct CanonicalVarKind(std::marker::PhantomData); + +pub struct TyKind(std::marker::PhantomData); + +pub trait IntoKind { + type Kind; + + // Required method + fn kind(self) -> Self::Kind; +} +pub trait Flags { + // Required methods + fn flags(&self) -> TypeFlags; + fn outer_exclusive_binder(&self) -> DebruijnIndex; +} +pub struct TypeFlags; + +pub trait Ty> { + // Required method + fn new_anon_bound( + interner: I, + debruijn: DebruijnIndex, + var: BoundVar + ) -> Self; +} + +pub trait PlaceholderLike { + // Required methods + fn universe(self) -> UniverseIndex; + fn var(self) -> BoundVar; + fn with_updated_universe(self, ui: UniverseIndex) -> Self; + fn new(ui: UniverseIndex, var: BoundVar) -> Self; +} + +pub struct UniverseIndex; + +pub struct BoundVar; + +pub struct ConstKind(std::marker::PhantomData); +pub trait Const> { + // Required method + fn new_anon_bound( + interner: I, + debruijn: DebruijnIndex, + var: BoundVar, + ty: I::Ty + ) -> Self; +} + +pub trait ConstTy { + // Required method + fn ty(self) -> I::Ty; +} + +pub struct DebruijnIndex; + +pub struct RegionKind(std::marker::PhantomData); +pub trait Region> { + // Required method + fn new_anon_bound( + interner: I, + debruijn: DebruijnIndex, + var: BoundVar + ) -> Self; +} + +pub trait TypeVisitor: Sized { + type Result: VisitorResult = (); + + // Provided methods + fn visit_binder>( + &mut self, + t: &I::Binder + ) -> Self::Result { unimplemented!() } + fn visit_ty(&mut self, t: I::Ty) -> Self::Result { unimplemented!() } + fn visit_region(&mut self, _r: I::Region) -> Self::Result { unimplemented!() } + fn visit_const(&mut self, c: I::Const) -> Self::Result { unimplemented!() } + fn visit_predicate(&mut self, p: I::Predicate) -> Self::Result { unimplemented!() } +} + +pub trait VisitorResult { + type Residual; + + // Required methods + fn output() -> Self; + fn from_residual(residual: Self::Residual) -> Self; + fn from_branch(b: ControlFlow) -> Self; + fn branch(self) -> ControlFlow; +} + +impl VisitorResult for () { + type Residual = (); + fn output() -> Self {} + fn from_residual(_: Self::Residual) -> Self {} + fn from_branch(_: ControlFlow) -> Self {} + fn branch(self) -> ControlFlow { ControlFlow::Continue(()) } +} + +pub struct WithInfcx<'a, Infcx: InferCtxtLike, T> { + pub data: T, + pub infcx: &'a Infcx, +} + +pub trait InferCtxtLike { + type Interner: Interner; + + // Required methods + fn interner(&self) -> Self::Interner; + fn universe_of_ty(&self, ty: TyVid) -> Option; + fn root_ty_var(&self, vid: TyVid) -> TyVid; + fn probe_ty_var( + &self, + vid: TyVid + ) -> Option<::Ty>; + fn universe_of_lt( + &self, + lt: ::InferRegion + ) -> Option; + fn opportunistic_resolve_lt_var( + &self, + vid: ::InferRegion + ) -> Option<::Region>; + fn universe_of_ct(&self, ct: ConstVid) -> Option; + fn root_ct_var(&self, vid: ConstVid) -> ConstVid; + fn probe_ct_var( + &self, + vid: ConstVid + ) -> Option<::Const>; +} + +pub struct TyVid; +pub struct ConstVid; diff --git a/tests/rustdoc-js/looks-like-rustc-interner.js b/tests/rustdoc-js/looks-like-rustc-interner.js new file mode 100644 index 0000000000000..a4806d2349929 --- /dev/null +++ b/tests/rustdoc-js/looks-like-rustc-interner.js @@ -0,0 +1,9 @@ +// https://github.com/rust-lang/rust/pull/122247 +// exact-check + +const EXPECTED = { + 'query': 'canonicalvarinfo, intoiterator -> intoiterator', + 'others': [ + { 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_infos' }, + ], +}; diff --git a/tests/rustdoc-js/looks-like-rustc-interner.rs b/tests/rustdoc-js/looks-like-rustc-interner.rs new file mode 100644 index 0000000000000..f304e28d95249 --- /dev/null +++ b/tests/rustdoc-js/looks-like-rustc-interner.rs @@ -0,0 +1,5 @@ +//@ aux-crate:interner=interner.rs +// https://github.com/rust-lang/rust/pull/122247 +extern crate interner; +#[doc(inline)] +pub use interner::*; From 7f427f86bd0db622f896bcdce04ce5f20658c071 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 6 Jan 2024 13:17:51 -0700 Subject: [PATCH 28/79] rustdoc-search: parse and search with ML-style HOF Option::map, for example, looks like this: option, (t -> u) -> option This syntax searches all of the HOFs in Rust: traits Fn, FnOnce, and FnMut, and bare fn primitives. --- .../rustdoc/src/read-documentation/search.md | 8 +- src/librustdoc/html/render/search_index.rs | 40 +- src/librustdoc/html/static/js/search.js | 166 ++++++-- tests/rustdoc-js-std/parser-errors.js | 4 +- tests/rustdoc-js-std/parser-hof.js | 376 ++++++++++++++++++ tests/rustdoc-js/hof.js | 94 +++++ tests/rustdoc-js/hof.rs | 12 + 7 files changed, 650 insertions(+), 50 deletions(-) create mode 100644 tests/rustdoc-js-std/parser-hof.js create mode 100644 tests/rustdoc-js/hof.js create mode 100644 tests/rustdoc-js/hof.rs diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index b5f4060f05905..d17e41bcde7de 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -63,11 +63,12 @@ Before describing the syntax in more detail, here's a few sample searches of the standard library and functions that are included in the results list: | Query | Results | -|-------|--------| +|-------|---------| | [`usize -> vec`][] | `slice::repeat` and `Vec::with_capacity` | | [`vec, vec -> bool`][] | `Vec::eq` | | [`option, fnonce -> option`][] | `Option::map` and `Option::and_then` | -| [`option, fnonce -> option`][] | `Option::filter` and `Option::inspect` | +| [`option, (fnonce (T) -> bool) -> option`][optionfilter] | `Option::filter` | +| [`option, (T -> bool) -> option`][optionfilter2] | `Option::filter` | | [`option -> default`][] | `Option::unwrap_or_default` | | [`stdout, [u8]`][stdoutu8] | `Stdout::write` | | [`any -> !`][] | `panic::panic_any` | @@ -77,7 +78,8 @@ the standard library and functions that are included in the results list: [`usize -> vec`]: ../../std/vec/struct.Vec.html?search=usize%20-%3E%20vec&filter-crate=std [`vec, vec -> bool`]: ../../std/vec/struct.Vec.html?search=vec,%20vec%20-%3E%20bool&filter-crate=std [`option, fnonce -> option`]: ../../std/vec/struct.Vec.html?search=option%2C%20fnonce%20->%20option&filter-crate=std -[`option, fnonce -> option`]: ../../std/vec/struct.Vec.html?search=option%2C%20fnonce%20->%20option&filter-crate=std +[optionfilter]: ../../std/vec/struct.Vec.html?search=option%2C+(fnonce+(T)+->+bool)+->+option&filter-crate=std +[optionfilter2]: ../../std/vec/struct.Vec.html?search=option%2C+(T+->+bool)+->+option&filter-crate=std [`option -> default`]: ../../std/vec/struct.Vec.html?search=option%20-%3E%20default&filter-crate=std [`any -> !`]: ../../std/vec/struct.Vec.html?search=any%20-%3E%20!&filter-crate=std [stdoutu8]: ../../std/vec/struct.Vec.html?search=stdout%2C%20[u8]&filter-crate=std diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index cb059082f85ba..f153a90832910 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -4,6 +4,7 @@ use std::collections::{BTreeMap, VecDeque}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; +use rustc_span::sym; use rustc_span::symbol::Symbol; use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer}; use thin_vec::ThinVec; @@ -566,6 +567,7 @@ fn get_index_type_id( // The type parameters are converted to generics in `simplify_fn_type` clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)), clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)), + clean::BareFunction(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Fn)), clean::Tuple(ref n) if n.is_empty() => { Some(RenderTypeId::Primitive(clean::PrimitiveType::Unit)) } @@ -584,7 +586,7 @@ fn get_index_type_id( } } // Not supported yet - clean::BareFunction(_) | clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, + clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, } } @@ -785,6 +787,42 @@ fn simplify_fn_type<'tcx, 'a>( ); } res.push(get_index_type(arg, ty_generics, rgen)); + } else if let Type::BareFunction(ref bf) = *arg { + let mut ty_generics = Vec::new(); + for ty in bf.decl.inputs.values.iter().map(|arg| &arg.type_) { + simplify_fn_type( + self_, + generics, + ty, + tcx, + recurse + 1, + &mut ty_generics, + rgen, + is_return, + cache, + ); + } + // The search index, for simplicity's sake, represents fn pointers and closures + // the same way: as a tuple for the parameters, and an associated type for the + // return type. + let mut ty_output = Vec::new(); + simplify_fn_type( + self_, + generics, + &bf.decl.output, + tcx, + recurse + 1, + &mut ty_output, + rgen, + is_return, + cache, + ); + let ty_bindings = vec![(RenderTypeId::AssociatedType(sym::Output), ty_output)]; + res.push(RenderType { + id: get_index_type_id(&arg, rgen), + bindings: Some(ty_bindings), + generics: Some(ty_generics), + }); } else { // This is not a type parameter. So for example if we have `T, U: Option`, and we're // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't. diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 7995a33f09f9b..1e163367b512a 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -272,6 +272,22 @@ function initSearch(rawSearchIndex) { * Special type name IDs for searching by both tuple and unit (`()` syntax). */ let typeNameIdOfTupleOrUnit; + /** + * Special type name IDs for searching `fn`. + */ + let typeNameIdOfFn; + /** + * Special type name IDs for searching `fnmut`. + */ + let typeNameIdOfFnMut; + /** + * Special type name IDs for searching `fnonce`. + */ + let typeNameIdOfFnOnce; + /** + * Special type name IDs for searching higher order functions (`->` syntax). + */ + let typeNameIdOfHof; /** * Add an item to the type Name->ID map, or, if one already exists, use it. @@ -464,6 +480,21 @@ function initSearch(rawSearchIndex) { } } + function makePrimitiveElement(name, extra) { + return Object.assign({ + name, + id: null, + fullPath: [name], + pathWithoutLast: [], + pathLast: name, + normalizedPathLast: name, + generics: [], + bindings: new Map(), + typeFilter: "primitive", + bindingName: null, + }, extra); + } + /** * @param {ParsedQuery} query * @param {ParserState} parserState @@ -501,18 +532,7 @@ function initSearch(rawSearchIndex) { } const bindingName = parserState.isInBinding; parserState.isInBinding = null; - return { - name: "never", - id: null, - fullPath: ["never"], - pathWithoutLast: [], - pathLast: "never", - normalizedPathLast: "never", - generics: [], - bindings: new Map(), - typeFilter: "primitive", - bindingName, - }; + return makePrimitiveElement("never", { bindingName }); } const quadcolon = /::\s*::/.exec(path); if (path.startsWith("::")) { @@ -671,28 +691,19 @@ function initSearch(rawSearchIndex) { let start = parserState.pos; let end; if ("[(".indexOf(parserState.userQuery[parserState.pos]) !== -1) { -let endChar = ")"; -let name = "()"; -let friendlyName = "tuple"; - -if (parserState.userQuery[parserState.pos] === "[") { - endChar = "]"; - name = "[]"; - friendlyName = "slice"; -} + let endChar = ")"; + let name = "()"; + let friendlyName = "tuple"; + + if (parserState.userQuery[parserState.pos] === "[") { + endChar = "]"; + name = "[]"; + friendlyName = "slice"; + } parserState.pos += 1; const { foundSeparator } = getItemsBefore(query, parserState, generics, endChar); const typeFilter = parserState.typeFilter; - const isInBinding = parserState.isInBinding; - if (typeFilter !== null && typeFilter !== "primitive") { - throw [ - "Invalid search type: primitive ", - name, - " and ", - typeFilter, - " both specified", - ]; - } + const bindingName = parserState.isInBinding; parserState.typeFilter = null; parserState.isInBinding = null; for (const gen of generics) { @@ -702,23 +713,26 @@ if (parserState.userQuery[parserState.pos] === "[") { } if (name === "()" && !foundSeparator && generics.length === 1 && typeFilter === null) { elems.push(generics[0]); + } else if (name === "()" && generics.length === 1 && generics[0].name === "->") { + // `primitive:(a -> b)` parser to `primitive:"->"` + // not `primitive:"()"<"->">` + generics[0].typeFilter = typeFilter; + elems.push(generics[0]); } else { + if (typeFilter !== null && typeFilter !== "primitive") { + throw [ + "Invalid search type: primitive ", + name, + " and ", + typeFilter, + " both specified", + ]; + } parserState.totalElems += 1; if (isInGenerics) { parserState.genericsElems += 1; } - elems.push({ - name: name, - id: null, - fullPath: [name], - pathWithoutLast: [], - pathLast: name, - normalizedPathLast: name, - generics, - bindings: new Map(), - typeFilter: "primitive", - bindingName: isInBinding, - }); + elems.push(makePrimitiveElement(name, { bindingName, generics })); } } else { const isStringElem = parserState.userQuery[start] === "\""; @@ -805,6 +819,19 @@ if (parserState.userQuery[parserState.pos] === "[") { const oldIsInBinding = parserState.isInBinding; parserState.isInBinding = null; + // ML-style Higher Order Function notation + // + // a way to search for any closure or fn pointer regardless of + // which closure trait is used + // + // Looks like this: + // + // `option, (t -> u) -> option` + // ^^^^^^ + // + // The Rust-style closure notation is implemented in getNextElem + let hofParameters = null; + let extra = ""; if (endChar === ">") { extra = "<"; @@ -825,6 +852,21 @@ if (parserState.userQuery[parserState.pos] === "[") { throw ["Unexpected ", endChar, " after ", "="]; } break; + } else if (endChar !== "" && isReturnArrow(parserState)) { + // ML-style HOF notation only works when delimited in something, + // otherwise a function arrow starts the return type of the top + if (parserState.isInBinding) { + throw ["Unexpected ", "->", " after ", "="]; + } + hofParameters = [...elems]; + elems.length = 0; + parserState.pos += 2; + foundStopChar = true; + foundSeparator = false; + continue; + } else if (c === " ") { + parserState.pos += 1; + continue; } else if (isSeparatorCharacter(c)) { parserState.pos += 1; foundStopChar = true; @@ -904,6 +946,27 @@ if (parserState.userQuery[parserState.pos] === "[") { // in any case. parserState.pos += 1; + if (hofParameters) { + // Commas in a HOF don't cause wrapping parens to become a tuple. + // If you want a one-tuple with a HOF in it, write `((a -> b),)`. + foundSeparator = false; + // HOFs can't have directly nested bindings. + if ([...elems, ...hofParameters].some(x => x.bindingName) || parserState.isInBinding) { + throw ["Unexpected ", "=", " within ", "->"]; + } + // HOFs are represented the same way closures are. + // The arguments are wrapped in a tuple, and the output + // is a binding, even though the compiler doesn't technically + // represent fn pointers that way. + const hofElem = makePrimitiveElement("->", { + generics: hofParameters, + bindings: new Map([["output", [...elems]]]), + typeFilter: null, + }); + elems.length = 0; + elems[0] = hofElem; + } + parserState.typeFilter = oldTypeFilter; parserState.isInBinding = oldIsInBinding; @@ -1635,6 +1698,12 @@ if (parserState.userQuery[parserState.pos] === "[") { ) { // () matches primitive:tuple or primitive:unit // if it matches, then we're fine, and this is an appropriate match candidate + } else if (queryElem.id === typeNameIdOfHof && + (fnType.id === typeNameIdOfFn || fnType.id === typeNameIdOfFnMut || + fnType.id === typeNameIdOfFnOnce) + ) { + // -> matches fn, fnonce, and fnmut + // if it matches, then we're fine, and this is an appropriate match candidate } else if (fnType.id !== queryElem.id || queryElem.id === null) { return false; } @@ -1829,6 +1898,7 @@ if (parserState.userQuery[parserState.pos] === "[") { typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 && // special case elem.id !== typeNameIdOfArrayOrSlice && elem.id !== typeNameIdOfTupleOrUnit + && elem.id !== typeNameIdOfHof ) { return row.id === elem.id || checkIfInList( row.generics, @@ -2991,7 +3061,7 @@ ${item.displayPath}${name}\ */ function buildFunctionTypeFingerprint(type, output, fps) { let input = type.id; - // All forms of `[]`/`()` get collapsed down to one thing in the bloom filter. + // All forms of `[]`/`()`/`->` get collapsed down to one thing in the bloom filter. // Differentiating between arrays and slices, if the user asks for it, is // still done in the matching algorithm. if (input === typeNameIdOfArray || input === typeNameIdOfSlice) { @@ -3000,6 +3070,10 @@ ${item.displayPath}${name}\ if (input === typeNameIdOfTuple || input === typeNameIdOfUnit) { input = typeNameIdOfTupleOrUnit; } + if (input === typeNameIdOfFn || input === typeNameIdOfFnMut || + input === typeNameIdOfFnOnce) { + input = typeNameIdOfHof; + } // http://burtleburtle.net/bob/hash/integer.html // ~~ is toInt32. It's used before adding, so // the number stays in safe integer range. @@ -3103,6 +3177,10 @@ ${item.displayPath}${name}\ typeNameIdOfUnit = buildTypeMapIndex("unit"); typeNameIdOfArrayOrSlice = buildTypeMapIndex("[]"); typeNameIdOfTupleOrUnit = buildTypeMapIndex("()"); + typeNameIdOfFn = buildTypeMapIndex("fn"); + typeNameIdOfFnMut = buildTypeMapIndex("fnmut"); + typeNameIdOfFnOnce = buildTypeMapIndex("fnonce"); + typeNameIdOfHof = buildTypeMapIndex("->"); // Function type fingerprints are 128-bit bloom filters that are used to // estimate the distance between function and query. diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index 16d171260dadb..8efb81841d47f 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -114,7 +114,7 @@ const PARSED = [ original: "(p -> p", returned: [], userQuery: "(p -> p", - error: "Unexpected `-` after `(`", + error: "Unclosed `(`", }, { query: "::a::b", @@ -330,7 +330,7 @@ const PARSED = [ original: 'a<->', returned: [], userQuery: 'a<->', - error: 'Unexpected `-` after `<`', + error: 'Unclosed `<`', }, { query: "a:", diff --git a/tests/rustdoc-js-std/parser-hof.js b/tests/rustdoc-js-std/parser-hof.js new file mode 100644 index 0000000000000..331c516e047a9 --- /dev/null +++ b/tests/rustdoc-js-std/parser-hof.js @@ -0,0 +1,376 @@ +const PARSED = [ + { + query: "(-> F

)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [], + bindings: [ + [ + "output", + [{ + name: "f", + fullPath: ["f"], + pathWithoutLast: [], + pathLast: "f", + generics: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + }, + ], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(-> F

)", + returned: [], + userQuery: "(-> f

)", + error: null, + }, + { + query: "(-> P)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [], + bindings: [ + [ + "output", + [{ + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(-> P)", + returned: [], + userQuery: "(-> p)", + error: null, + }, + { + query: "(->,a)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(->,a)", + returned: [], + userQuery: "(->,a)", + error: null, + }, + { + query: "(F

->)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [{ + name: "f", + fullPath: ["f"], + pathWithoutLast: [], + pathLast: "f", + generics: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + }, + ], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(F

->)", + returned: [], + userQuery: "(f

->)", + error: null, + }, + { + query: "(P ->)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [{ + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(P ->)", + returned: [], + userQuery: "(p ->)", + error: null, + }, + { + query: "(,a->)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(,a->)", + returned: [], + userQuery: "(,a->)", + error: null, + }, + { + query: "(aaaaa->a)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [{ + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(aaaaa->a)", + returned: [], + userQuery: "(aaaaa->a)", + error: null, + }, + { + query: "(aaaaa, b -> a)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(aaaaa, b -> a)", + returned: [], + userQuery: "(aaaaa, b -> a)", + error: null, + }, + { + query: "primitive:(aaaaa, b -> a)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: 1, + }], + foundElems: 1, + original: "primitive:(aaaaa, b -> a)", + returned: [], + userQuery: "primitive:(aaaaa, b -> a)", + error: null, + }, + { + query: "x, trait:(aaaaa, b -> a)", + elems: [ + { + name: "x", + fullPath: ["x"], + pathWithoutLast: [], + pathLast: "x", + generics: [], + typeFilter: -1, + }, + { + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: 10, + } + ], + foundElems: 2, + original: "x, trait:(aaaaa, b -> a)", + returned: [], + userQuery: "x, trait:(aaaaa, b -> a)", + error: null, + }, +]; diff --git a/tests/rustdoc-js/hof.js b/tests/rustdoc-js/hof.js new file mode 100644 index 0000000000000..1f5d2bfb6666c --- /dev/null +++ b/tests/rustdoc-js/hof.js @@ -0,0 +1,94 @@ +// exact-check + +const EXPECTED = [ + // ML-style higher-order function notation + { + 'query': 'bool, (u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': 'u8, (u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': 'i8, (u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_mut"}, + ], + }, + { + 'query': 'char, (u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_"}, + ], + }, + { + 'query': '(first -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': '(second -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': '(third -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_mut"}, + ], + }, + { + 'query': '(u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_"}, + {"path": "hof", "name": "fn_ptr"}, + {"path": "hof", "name": "fn_mut"}, + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': 'u32 -> !', + // not a HOF query + 'others': [], + }, + { + 'query': '(str, str -> i8) -> ()', + 'others': [ + {"path": "hof", "name": "multiple"}, + ], + }, + { + 'query': '(str ->) -> ()', + 'others': [ + {"path": "hof", "name": "multiple"}, + ], + }, + { + 'query': '(-> i8) -> ()', + 'others': [ + {"path": "hof", "name": "multiple"}, + ], + }, + { + 'query': '(str -> str) -> ()', + // params and return are not the same + 'others': [], + }, + { + 'query': '(i8 ->) -> ()', + // params and return are not the same + 'others': [], + }, + { + 'query': '(-> str) -> ()', + // params and return are not the same + 'others': [], + }, +]; diff --git a/tests/rustdoc-js/hof.rs b/tests/rustdoc-js/hof.rs new file mode 100644 index 0000000000000..4d2c6e331cac0 --- /dev/null +++ b/tests/rustdoc-js/hof.rs @@ -0,0 +1,12 @@ +#![feature(never_type)] + +pub struct First(T); +pub struct Second(T); +pub struct Third(T); + +pub fn fn_ptr(_: fn (First) -> !, _: bool) {} +pub fn fn_once(_: impl FnOnce (Second) -> !, _: u8) {} +pub fn fn_mut(_: impl FnMut (Third) -> !, _: i8) {} +pub fn fn_(_: impl Fn (u32) -> !, _: char) {} + +pub fn multiple(_: impl Fn(&'static str, &'static str) -> i8) {} From d38527eb82fd10ee5578b541b06904611f6f8427 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 6 Jan 2024 13:45:52 -0700 Subject: [PATCH 29/79] rustdoc: clean up search.js by removing empty sort case It's going to be a no-op on the empty list anyway (we have plenty of test cases that return nothing) so why send extra code? --- src/librustdoc/html/static/js/search.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 1e163367b512a..f8d1714e86603 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1321,11 +1321,6 @@ function initSearch(rawSearchIndex) { * @returns {[ResultObject]} */ function sortResults(results, isType, preferredCrate) { - // if there are no results then return to default and fail - if (results.size === 0) { - return []; - } - const userQuery = parsedQuery.userQuery; const result_list = []; for (const result of results.values()) { From 23e931fd076cc1d02a34b3f9bd9a64f92c8f4289 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 6 Jan 2024 14:07:12 -0700 Subject: [PATCH 30/79] rustdoc: use `const` for the special type name ids Initialize them before the search index is loaded. --- src/librustdoc/html/static/js/search.js | 36 ++++++++----------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index f8d1714e86603..6c285ba6f5102 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -245,49 +245,49 @@ function initSearch(rawSearchIndex) { * * @type {Map} */ - let typeNameIdMap; + const typeNameIdMap = new Map(); const ALIASES = new Map(); /** * Special type name IDs for searching by array. */ - let typeNameIdOfArray; + const typeNameIdOfArray = buildTypeMapIndex("array"); /** * Special type name IDs for searching by slice. */ - let typeNameIdOfSlice; + const typeNameIdOfSlice = buildTypeMapIndex("slice"); /** * Special type name IDs for searching by both array and slice (`[]` syntax). */ - let typeNameIdOfArrayOrSlice; + const typeNameIdOfArrayOrSlice = buildTypeMapIndex("[]"); /** * Special type name IDs for searching by tuple. */ - let typeNameIdOfTuple; + const typeNameIdOfTuple = buildTypeMapIndex("tuple"); /** * Special type name IDs for searching by unit. */ - let typeNameIdOfUnit; + const typeNameIdOfUnit = buildTypeMapIndex("unit"); /** * Special type name IDs for searching by both tuple and unit (`()` syntax). */ - let typeNameIdOfTupleOrUnit; + const typeNameIdOfTupleOrUnit = buildTypeMapIndex("()"); /** * Special type name IDs for searching `fn`. */ - let typeNameIdOfFn; + const typeNameIdOfFn = buildTypeMapIndex("fn"); /** * Special type name IDs for searching `fnmut`. */ - let typeNameIdOfFnMut; + const typeNameIdOfFnMut = buildTypeMapIndex("fnmut"); /** * Special type name IDs for searching `fnonce`. */ - let typeNameIdOfFnOnce; + const typeNameIdOfFnOnce = buildTypeMapIndex("fnonce"); /** * Special type name IDs for searching higher order functions (`->` syntax). */ - let typeNameIdOfHof; + const typeNameIdOfHof = buildTypeMapIndex("->"); /** * Add an item to the type Name->ID map, or, if one already exists, use it. @@ -3159,24 +3159,10 @@ ${item.displayPath}${name}\ */ function buildIndex(rawSearchIndex) { searchIndex = []; - typeNameIdMap = new Map(); const charA = "A".charCodeAt(0); let currentIndex = 0; let id = 0; - // Initialize type map indexes for primitive list types - // that can be searched using `[]` syntax. - typeNameIdOfArray = buildTypeMapIndex("array"); - typeNameIdOfSlice = buildTypeMapIndex("slice"); - typeNameIdOfTuple = buildTypeMapIndex("tuple"); - typeNameIdOfUnit = buildTypeMapIndex("unit"); - typeNameIdOfArrayOrSlice = buildTypeMapIndex("[]"); - typeNameIdOfTupleOrUnit = buildTypeMapIndex("()"); - typeNameIdOfFn = buildTypeMapIndex("fn"); - typeNameIdOfFnMut = buildTypeMapIndex("fnmut"); - typeNameIdOfFnOnce = buildTypeMapIndex("fnonce"); - typeNameIdOfHof = buildTypeMapIndex("->"); - // Function type fingerprints are 128-bit bloom filters that are used to // estimate the distance between function and query. // This loop counts the number of items to allocate a fingerprint for. From 7b926555b71032889ea5079aa642885e63fe51c2 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Sat, 6 Jan 2024 16:01:10 -0700 Subject: [PATCH 31/79] rustdoc-search: add search query syntax `Fn(T) -> U` This is implemented, in addition to the ML-style one, because Rust does it. If we don't, we'll never hear the end of it. This commit also refactors some duplicate parts of the parser into a dedicated function. --- .../rustdoc/src/read-documentation/search.md | 46 ++- src/librustdoc/html/static/js/search.js | 113 +++--- tests/rustdoc-js-std/parser-errors.js | 15 +- tests/rustdoc-js-std/parser-hof.js | 336 ++++++++++++++++++ tests/rustdoc-js-std/parser-weird-queries.js | 9 - tests/rustdoc-js/hof.js | 92 ++++- 6 files changed, 530 insertions(+), 81 deletions(-) diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index d17e41bcde7de..e2def14b357ea 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -153,16 +153,26 @@ will match these queries: But it *does not* match `Result` or `Result>`. +To search for a function that accepts a function as a parameter, +like `Iterator::all`, wrap the nested signature in parenthesis, +as in [`Iterator, (T -> bool) -> bool`][iterator-all]. +You can also search for a specific closure trait, +such as `Iterator, (FnMut(T) -> bool) -> bool`, +but you need to know which one you want. + +[iterator-all]: ../../std/vec/struct.Vec.html?search=Iterator%2C+(T+->+bool)+->+bool&filter-crate=std + ### Primitives with Special Syntax -| Shorthand | Explicit names | -| --------- | ------------------------------------------------ | -| `[]` | `primitive:slice` and/or `primitive:array` | -| `[T]` | `primitive:slice` and/or `primitive:array` | -| `()` | `primitive:unit` and/or `primitive:tuple` | -| `(T)` | `T` | -| `(T,)` | `primitive:tuple` | -| `!` | `primitive:never` | +| Shorthand | Explicit names | +| ---------------- | ------------------------------------------------- | +| `[]` | `primitive:slice` and/or `primitive:array` | +| `[T]` | `primitive:slice` and/or `primitive:array` | +| `()` | `primitive:unit` and/or `primitive:tuple` | +| `(T)` | `T` | +| `(T,)` | `primitive:tuple` | +| `!` | `primitive:never` | +| `(T, U -> V, W)` | `fn(T, U) -> (V, W)`, `Fn`, `FnMut`, and `FnOnce` | When searching for `[]`, Rustdoc will return search results with either slices or arrays. If you know which one you want, you can force it to return results @@ -182,6 +192,10 @@ results for types that match tuples, even though it also matches the type on its own. That is, `(u32)` matches `(u32,)` for the exact same reason that it also matches `Result`. +The `->` operator has lower precedence than comma. If it's not wrapped +in brackets, it delimits the return value for the function being searched for. +To search for functions that take functions as parameters, use parenthesis. + ### Limitations and quirks of type-based search Type-based search is still a buggy, experimental, work-in-progress feature. @@ -220,9 +234,6 @@ Most of these limitations should be addressed in future version of Rustdoc. * Searching for lifetimes is not supported. - * It's impossible to search for closures based on their parameters or - return values. - * It's impossible to search based on the length of an array. ## Item filtering @@ -239,19 +250,21 @@ Item filters can be used in both name-based and type signature-based searches. ```text ident = *(ALPHA / DIGIT / "_") -path = ident *(DOUBLE-COLON ident) [!] +path = ident *(DOUBLE-COLON ident) [BANG] slice-like = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET tuple-like = OPEN-PAREN [ nonempty-arg-list ] CLOSE-PAREN -arg = [type-filter *WS COLON *WS] (path [generics] / slice-like / tuple-like / [!]) +arg = [type-filter *WS COLON *WS] (path [generics] / slice-like / tuple-like) type-sep = COMMA/WS *(COMMA/WS) -nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) +nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) [ return-args ] generic-arg-list = *(type-sep) arg [ EQUAL arg ] *(type-sep arg [ EQUAL arg ]) *(type-sep) -generics = OPEN-ANGLE-BRACKET [ generic-arg-list ] *(type-sep) +normal-generics = OPEN-ANGLE-BRACKET [ generic-arg-list ] *(type-sep) CLOSE-ANGLE-BRACKET +fn-like-generics = OPEN-PAREN [ nonempty-arg-list ] CLOSE-PAREN [ RETURN-ARROW arg ] +generics = normal-generics / fn-like-generics return-args = RETURN-ARROW *(type-sep) nonempty-arg-list exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ] -type-search = [ nonempty-arg-list ] [ return-args ] +type-search = [ nonempty-arg-list ] query = *WS (exact-search / type-search) *WS @@ -296,6 +309,7 @@ QUOTE = %x22 COMMA = "," RETURN-ARROW = "->" EQUAL = "=" +BANG = "!" ALPHA = %x41-5A / %x61-7A ; A-Z / a-z DIGIT = %x30-39 diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 6c285ba6f5102..73ac5608479b1 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1,3 +1,4 @@ +// ignore-tidy-filelength /* global addClass, getNakedUrl, getSettingValue */ /* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi, exports */ @@ -578,7 +579,10 @@ function initSearch(rawSearchIndex) { // Syntactically, bindings are parsed as generics, // but the query engine treats them differently. if (gen.bindingName !== null) { - bindings.set(gen.bindingName.name, [gen, ...gen.bindingName.generics]); + if (gen.name !== null) { + gen.bindingName.generics.unshift(gen); + } + bindings.set(gen.bindingName.name, gen.bindingName.generics); return false; } return true; @@ -678,6 +682,38 @@ function initSearch(rawSearchIndex) { return end; } + function getFilteredNextElem(query, parserState, elems, isInGenerics) { + const start = parserState.pos; + if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { + throw ["Expected type filter before ", ":"]; + } + getNextElem(query, parserState, elems, isInGenerics); + if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { + if (parserState.typeFilter !== null) { + throw [ + "Unexpected ", + ":", + " (expected path after type filter ", + parserState.typeFilter + ":", + ")", + ]; + } + if (elems.length === 0) { + throw ["Expected type filter before ", ":"]; + } else if (query.literalSearch) { + throw ["Cannot use quotes on type filter"]; + } + // The type filter doesn't count as an element since it's a modifier. + const typeFilterElem = elems.pop(); + checkExtraTypeFilterCharacters(start, parserState); + parserState.typeFilter = typeFilterElem.name; + parserState.pos += 1; + parserState.totalElems -= 1; + query.literalSearch = false; + getNextElem(query, parserState, elems, isInGenerics); + } + } + /** * @param {ParsedQuery} query * @param {ParserState} parserState @@ -752,6 +788,32 @@ function initSearch(rawSearchIndex) { } parserState.pos += 1; getItemsBefore(query, parserState, generics, ">"); + } else if (parserState.pos < parserState.length && + parserState.userQuery[parserState.pos] === "(" + ) { + if (start >= end) { + throw ["Found generics without a path"]; + } + if (parserState.isInBinding) { + throw ["Unexpected ", "(", " after ", "="]; + } + parserState.pos += 1; + const typeFilter = parserState.typeFilter; + parserState.typeFilter = null; + getItemsBefore(query, parserState, generics, ")"); + skipWhitespace(parserState); + if (isReturnArrow(parserState)) { + parserState.pos += 2; + skipWhitespace(parserState); + getFilteredNextElem(query, parserState, generics, isInGenerics); + generics[generics.length - 1].bindingName = makePrimitiveElement("output"); + } else { + generics.push(makePrimitiveElement(null, { + bindingName: makePrimitiveElement("output"), + typeFilter: null, + })); + } + parserState.typeFilter = typeFilter; } if (isStringElem) { skipWhitespace(parserState); @@ -811,7 +873,6 @@ function initSearch(rawSearchIndex) { function getItemsBefore(query, parserState, elems, endChar) { let foundStopChar = true; let foundSeparator = false; - let start = parserState.pos; // If this is a generic, keep the outer item's type filter around. const oldTypeFilter = parserState.typeFilter; @@ -874,24 +935,6 @@ function initSearch(rawSearchIndex) { continue; } else if (c === ":" && isPathStart(parserState)) { throw ["Unexpected ", "::", ": paths cannot start with ", "::"]; - } else if (c === ":") { - if (parserState.typeFilter !== null) { - throw ["Unexpected ", ":"]; - } - if (elems.length === 0) { - throw ["Expected type filter before ", ":"]; - } else if (query.literalSearch) { - throw ["Cannot use quotes on type filter"]; - } - // The type filter doesn't count as an element since it's a modifier. - const typeFilterElem = elems.pop(); - checkExtraTypeFilterCharacters(start, parserState); - parserState.typeFilter = typeFilterElem.name; - parserState.pos += 1; - parserState.totalElems -= 1; - query.literalSearch = false; - foundStopChar = true; - continue; } else if (isEndCharacter(c)) { throw ["Unexpected ", c, " after ", extra]; } @@ -926,8 +969,7 @@ function initSearch(rawSearchIndex) { ]; } const posBefore = parserState.pos; - start = parserState.pos; - getNextElem(query, parserState, elems, endChar !== ""); + getFilteredNextElem(query, parserState, elems, endChar !== ""); if (endChar !== "" && parserState.pos >= parserState.length) { throw ["Unclosed ", extra]; } @@ -1004,7 +1046,6 @@ function initSearch(rawSearchIndex) { */ function parseInput(query, parserState) { let foundStopChar = true; - let start = parserState.pos; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; @@ -1022,29 +1063,6 @@ function initSearch(rawSearchIndex) { throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]]; } throw ["Unexpected ", c]; - } else if (c === ":" && !isPathStart(parserState)) { - if (parserState.typeFilter !== null) { - throw [ - "Unexpected ", - ":", - " (expected path after type filter ", - parserState.typeFilter + ":", - ")", - ]; - } else if (query.elems.length === 0) { - throw ["Expected type filter before ", ":"]; - } else if (query.literalSearch) { - throw ["Cannot use quotes on type filter"]; - } - // The type filter doesn't count as an element since it's a modifier. - const typeFilterElem = query.elems.pop(); - checkExtraTypeFilterCharacters(start, parserState); - parserState.typeFilter = typeFilterElem.name; - parserState.pos += 1; - parserState.totalElems -= 1; - query.literalSearch = false; - foundStopChar = true; - continue; } else if (c === " ") { skipWhitespace(parserState); continue; @@ -1080,8 +1098,7 @@ function initSearch(rawSearchIndex) { ]; } const before = query.elems.length; - start = parserState.pos; - getNextElem(query, parserState, query.elems, false); + getFilteredNextElem(query, parserState, query.elems, false); if (query.elems.length === before) { // Nothing was added, weird... Let's increase the position to not remain stuck. parserState.pos += 1; diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index 8efb81841d47f..ffd169812b631 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -195,7 +195,7 @@ const PARSED = [ original: "a (b:", returned: [], userQuery: "a (b:", - error: "Expected `,`, `:` or `->`, found `(`", + error: "Unclosed `(`", }, { query: "_:", @@ -357,7 +357,16 @@ const PARSED = [ original: "a,:", returned: [], userQuery: "a,:", - error: 'Unexpected `,` in type filter (before `:`)', + error: 'Expected type filter before `:`', + }, + { + query: "a!:", + elems: [], + foundElems: 0, + original: "a!:", + returned: [], + userQuery: "a!:", + error: 'Unexpected `!` in type filter (before `:`)', }, { query: " a<> :", @@ -366,7 +375,7 @@ const PARSED = [ original: "a<> :", returned: [], userQuery: "a<> :", - error: 'Unexpected `<` in type filter (before `:`)', + error: 'Expected `,`, `:` or `->` after `>`, found `:`', }, { query: "mod : :", diff --git a/tests/rustdoc-js-std/parser-hof.js b/tests/rustdoc-js-std/parser-hof.js index 331c516e047a9..0b99c45b7a922 100644 --- a/tests/rustdoc-js-std/parser-hof.js +++ b/tests/rustdoc-js-std/parser-hof.js @@ -1,4 +1,5 @@ const PARSED = [ + // ML-style HOF { query: "(-> F

)", elems: [{ @@ -373,4 +374,339 @@ const PARSED = [ userQuery: "x, trait:(aaaaa, b -> a)", error: null, }, + // Rust-style HOF + { + query: "Fn () -> F

", + elems: [{ + name: "fn", + fullPath: ["fn"], + pathWithoutLast: [], + pathLast: "fn", + generics: [], + bindings: [ + [ + "output", + [{ + name: "f", + fullPath: ["f"], + pathWithoutLast: [], + pathLast: "f", + generics: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + }, + ], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "Fn () -> F

", + returned: [], + userQuery: "fn () -> f

", + error: null, + }, + { + query: "FnMut() -> P", + elems: [{ + name: "fnmut", + fullPath: ["fnmut"], + pathWithoutLast: [], + pathLast: "fnmut", + generics: [], + bindings: [ + [ + "output", + [{ + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "FnMut() -> P", + returned: [], + userQuery: "fnmut() -> p", + error: null, + }, + { + query: "(FnMut() -> P)", + elems: [{ + name: "fnmut", + fullPath: ["fnmut"], + pathWithoutLast: [], + pathLast: "fnmut", + generics: [], + bindings: [ + [ + "output", + [{ + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(FnMut() -> P)", + returned: [], + userQuery: "(fnmut() -> p)", + error: null, + }, + { + query: "Fn(F

)", + elems: [{ + name: "fn", + fullPath: ["fn"], + pathWithoutLast: [], + pathLast: "fn", + generics: [{ + name: "f", + fullPath: ["f"], + pathWithoutLast: [], + pathLast: "f", + generics: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + }, + ], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "Fn(F

)", + returned: [], + userQuery: "fn(f

)", + error: null, + }, + { + query: "primitive:fnonce(aaaaa, b) -> a", + elems: [{ + name: "fnonce", + fullPath: ["fnonce"], + pathWithoutLast: [], + pathLast: "fnonce", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: 1, + }], + foundElems: 1, + original: "primitive:fnonce(aaaaa, b) -> a", + returned: [], + userQuery: "primitive:fnonce(aaaaa, b) -> a", + error: null, + }, + { + query: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", + elems: [{ + name: "fnonce", + fullPath: ["fnonce"], + pathWithoutLast: [], + pathLast: "fnonce", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: 0, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: 10, + }], + ], + ], + typeFilter: 1, + }], + foundElems: 1, + original: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", + returned: [], + userQuery: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", + error: null, + }, + { + query: "x, trait:fn(aaaaa, b -> a)", + elems: [ + { + name: "x", + fullPath: ["x"], + pathWithoutLast: [], + pathLast: "x", + generics: [], + typeFilter: -1, + }, + { + name: "fn", + fullPath: ["fn"], + pathWithoutLast: [], + pathLast: "fn", + generics: [ + { + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [], + ] + ], + typeFilter: 10, + } + ], + foundElems: 2, + original: "x, trait:fn(aaaaa, b -> a)", + returned: [], + userQuery: "x, trait:fn(aaaaa, b -> a)", + error: null, + }, + { + query: 'a,b(c)', + elems: [ + { + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [{ + name: "c", + fullPath: ["c"], + pathWithoutLast: [], + pathLast: "c", + generics: [], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ] + ], + typeFilter: -1, + } + ], + foundElems: 2, + original: "a,b(c)", + returned: [], + userQuery: "a,b(c)", + error: null, + }, ]; diff --git a/tests/rustdoc-js-std/parser-weird-queries.js b/tests/rustdoc-js-std/parser-weird-queries.js index 26b8c32d68052..499b82a346948 100644 --- a/tests/rustdoc-js-std/parser-weird-queries.js +++ b/tests/rustdoc-js-std/parser-weird-queries.js @@ -37,15 +37,6 @@ const PARSED = [ userQuery: "a b", error: null, }, - { - query: 'a,b(c)', - elems: [], - foundElems: 0, - original: "a,b(c)", - returned: [], - userQuery: "a,b(c)", - error: "Expected `,`, `:` or `->`, found `(`", - }, { query: 'aaa,a', elems: [ diff --git a/tests/rustdoc-js/hof.js b/tests/rustdoc-js/hof.js index 1f5d2bfb6666c..5e6c9d83c7c7f 100644 --- a/tests/rustdoc-js/hof.js +++ b/tests/rustdoc-js/hof.js @@ -1,6 +1,12 @@ // exact-check const EXPECTED = [ + // not a HOF query + { + 'query': 'u32 -> !', + 'others': [], + }, + // ML-style higher-order function notation { 'query': 'bool, (u32 -> !) -> ()', @@ -53,11 +59,6 @@ const EXPECTED = [ {"path": "hof", "name": "fn_once"}, ], }, - { - 'query': 'u32 -> !', - // not a HOF query - 'others': [], - }, { 'query': '(str, str -> i8) -> ()', 'others': [ @@ -91,4 +92,85 @@ const EXPECTED = [ // params and return are not the same 'others': [], }, + + // Rust-style higher-order function notation + { + 'query': 'bool, fn(u32) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': 'u8, fnonce(u32) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': 'u8, fn(u32) -> ! -> ()', + // fnonce != fn + 'others': [], + }, + { + 'query': 'i8, fnmut(u32) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_mut"}, + ], + }, + { + 'query': 'i8, fn(u32) -> ! -> ()', + // fnmut != fn + 'others': [], + }, + { + 'query': 'char, fn(u32) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_"}, + ], + }, + { + 'query': 'char, fnmut(u32) -> ! -> ()', + // fn != fnmut + 'others': [], + }, + { + 'query': 'fn(first) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': 'fnonce(second) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': 'fnmut(third) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_mut"}, + ], + }, + { + 'query': 'fn(u32) -> ! -> ()', + 'others': [ + // fn matches primitive:fn and trait:Fn + {"path": "hof", "name": "fn_"}, + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': 'trait:fn(u32) -> ! -> ()', + 'others': [ + // fn matches primitive:fn and trait:Fn + {"path": "hof", "name": "fn_"}, + ], + }, + { + 'query': 'primitive:fn(u32) -> ! -> ()', + 'others': [ + // fn matches primitive:fn and trait:Fn + {"path": "hof", "name": "fn_ptr"}, + ], + }, ]; From c076509d8a0c685df1105bf81c65f5fe21c83aa8 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Fri, 1 Mar 2024 11:00:33 -0800 Subject: [PATCH 32/79] Add methods to create constants I've been experimenting with transforming the StableMIR to instrument the code with potential UB checks. The modified body will only be used by our analysis tool, however, constants in StableMIR must be backed by rustc constants. Thus, I'm adding a few functions to build constants, such as building string and other primitives. --- compiler/rustc_smir/src/rustc_smir/context.rs | 58 ++++++++++++++++--- compiler/stable_mir/src/compiler_interface.rs | 17 ++++-- compiler/stable_mir/src/ty.rs | 27 ++++++++- 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 158d62a4830ff..26039a865f47f 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -23,7 +23,8 @@ use stable_mir::mir::Body; use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, - ForeignItemKind, GenericArgs, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, VariantDef, + ForeignItemKind, GenericArgs, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, UintTy, + VariantDef, }; use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol}; use std::cell::RefCell; @@ -341,15 +342,56 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } - fn usize_to_const(&self, val: u64) -> Result { + fn try_new_const_zst(&self, ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); - let ty = tables.tcx.types.usize; + let tcx = tables.tcx; + let ty_internal = ty.internal(&mut *tables, tcx); + let size = tables + .tcx + .layout_of(ParamEnv::empty().and(ty_internal)) + .map_err(|err| { + Error::new(format!( + "Cannot create a zero-sized constant for type `{ty_internal}`: {err}" + )) + })? + .size; + if size.bytes() != 0 { + return Err(Error::new(format!( + "Cannot create a zero-sized constant for type `{ty_internal}`: \ + Type `{ty_internal}` has {} bytes", + size.bytes() + ))); + } + + Ok(ty::Const::zero_sized(tables.tcx, ty_internal).stable(&mut *tables)) + } + + fn new_const_str(&self, value: &str) -> Const { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let ty = ty::Ty::new_static_str(tcx); + let bytes = value.as_bytes(); + let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes); + + ty::Const::new_value(tcx, val_tree, ty).stable(&mut *tables) + } + + fn new_const_bool(&self, value: bool) -> Const { + let mut tables = self.0.borrow_mut(); + ty::Const::from_bool(tables.tcx, value).stable(&mut *tables) + } + + fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let ty = ty::Ty::new_uint(tcx, uint_ty.internal(&mut *tables, tcx)); let size = tables.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap().size; - let scalar = ScalarInt::try_from_uint(val, size).ok_or_else(|| { - Error::new(format!("Value overflow: cannot convert `{val}` to usize.")) + // We don't use Const::from_bits since it doesn't have any error checking. + let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| { + Error::new(format!("Value overflow: cannot convert `{value}` to `{ty}`.")) })?; - Ok(rustc_middle::ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty) + Ok(ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty) .stable(&mut *tables)) } @@ -556,7 +598,9 @@ impl<'tcx> Context for TablesWrapper<'tcx> { global_alloc: &GlobalAlloc, ) -> Option { let mut tables = self.0.borrow_mut(); - let GlobalAlloc::VTable(ty, trait_ref) = global_alloc else { return None }; + let GlobalAlloc::VTable(ty, trait_ref) = global_alloc else { + return None; + }; let tcx = tables.tcx; let alloc_id = tables.tcx.vtable_allocation(( ty.internal(&mut *tables, tcx), diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 0f7d8d7e083bf..852d57ab14122 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -14,7 +14,7 @@ use crate::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, ImplDef, ImplTrait, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, Ty, TyKind, - VariantDef, + UintTy, VariantDef, }; use crate::{ mir, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, ItemKind, @@ -101,8 +101,17 @@ pub trait Context { /// Evaluate constant as a target usize. fn eval_target_usize(&self, cnst: &Const) -> Result; - /// Create a target usize constant for the given value. - fn usize_to_const(&self, val: u64) -> Result; + /// Create a new zero-sized constant. + fn try_new_const_zst(&self, ty: Ty) -> Result; + + /// Create a new constant that represents the given string value. + fn new_const_str(&self, value: &str) -> Const; + + /// Create a new constant that represents the given boolean value. + fn new_const_bool(&self, value: bool) -> Const; + + /// Create a new constant that represents the given value. + fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; /// Create a new type from the given kind. fn new_rigid_ty(&self, kind: RigidTy) -> Ty; @@ -199,7 +208,7 @@ pub trait Context { // A thread local variable that stores a pointer to the tables mapping between TyCtxt // datastructures and stable MIR datastructures -scoped_thread_local! (static TLV: Cell<*const ()>); +scoped_thread_local!(static TLV: Cell<*const ()>); pub fn run(context: &dyn Context, f: F) -> Result where diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 86cc748eaec93..a337675202871 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -128,13 +128,38 @@ impl Const { /// Creates an interned usize constant. fn try_from_target_usize(val: u64) -> Result { - with(|cx| cx.usize_to_const(val)) + with(|cx| cx.try_new_const_uint(val.into(), UintTy::Usize)) } /// Try to evaluate to a target `usize`. pub fn eval_target_usize(&self) -> Result { with(|cx| cx.eval_target_usize(self)) } + + /// Create a constant that represents a new zero-sized constant of type T. + /// Fails if the type is not a ZST or if it doesn't have a known size. + pub fn try_new_zero_sized(ty: Ty) -> Result { + with(|cx| cx.try_new_const_zst(ty)) + } + + /// Build a new constant that represents the given string. + /// + /// Note that there is no guarantee today about duplication of the same constant. + /// I.e.: Calling this function multiple times with the same argument may or may not return + /// the same allocation. + pub fn from_str(value: &str) -> Const { + with(|cx| cx.new_const_str(value)) + } + + /// Build a new constant that represents the given boolean value. + pub fn from_bool(value: bool) -> Const { + with(|cx| cx.new_const_bool(value)) + } + + /// Build a new constant that represents the given unsigned integer. + pub fn try_from_uint(value: u128, uint_ty: UintTy) -> Result { + with(|cx| cx.try_new_const_uint(value, uint_ty)) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] From 893a9107b9f8b2038d77648a2ca4b06f36d3009d Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Tue, 12 Mar 2024 12:55:18 -0700 Subject: [PATCH 33/79] Add a test to SMIR body transformation --- .../ui-fulldeps/stable-mir/check_transform.rs | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 tests/ui-fulldeps/stable-mir/check_transform.rs diff --git a/tests/ui-fulldeps/stable-mir/check_transform.rs b/tests/ui-fulldeps/stable-mir/check_transform.rs new file mode 100644 index 0000000000000..e7d852a27df0e --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_transform.rs @@ -0,0 +1,147 @@ +//@ run-pass +//! Test a few methods to transform StableMIR. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] +#![feature(ascii_char, ascii_char_variants)] + +extern crate rustc_hir; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_smir::rustc_internal; +use stable_mir::mir::alloc::GlobalAlloc; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::{Body, Constant, Operand, Rvalue, StatementKind, TerminatorKind}; +use stable_mir::ty::{Const, ConstantKind}; +use stable_mir::{CrateDef, CrateItems, ItemKind}; +use std::convert::TryFrom; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "input"; + +/// This function uses the Stable MIR APIs to transform the MIR. +fn test_transform() -> ControlFlow<()> { + // Find items in the local crate. + let items = stable_mir::all_local_items(); + + // Test fn_abi + let target_fn = *get_item(&items, (ItemKind::Fn, "dummy")).unwrap(); + let instance = Instance::try_from(target_fn).unwrap(); + let body = instance.body().unwrap(); + check_msg(&body, "oops"); + + let new_msg = "new panic message"; + let new_body = change_panic_msg(body, new_msg); + check_msg(&new_body, new_msg); + + ControlFlow::Continue(()) +} + +/// Check that the body panic message matches the given message. +fn check_msg(body: &Body, expected: &str) { + let msg = body + .blocks + .iter() + .find_map(|bb| match &bb.terminator.kind { + TerminatorKind::Call { args, .. } => { + assert_eq!(args.len(), 1, "Expected panic message, but found {args:?}"); + let msg_const = match &args[0] { + Operand::Constant(msg_const) => msg_const, + Operand::Copy(place) | Operand::Move(place) => { + assert!(place.projection.is_empty()); + bb.statements + .iter() + .find_map(|stmt| match &stmt.kind { + StatementKind::Assign( + destination, + Rvalue::Use(Operand::Constant(msg_const)), + ) if destination == place => Some(msg_const), + _ => None, + }) + .unwrap() + } + }; + let ConstantKind::Allocated(alloc) = msg_const.literal.kind() else { + unreachable!() + }; + assert_eq!(alloc.provenance.ptrs.len(), 1); + + let alloc_prov_id = alloc.provenance.ptrs[0].1 .0; + let GlobalAlloc::Memory(val) = GlobalAlloc::from(alloc_prov_id) else { + unreachable!() + }; + let bytes = val.raw_bytes().unwrap(); + Some(std::str::from_utf8(&bytes).unwrap().to_string()) + } + _ => None, + }) + .expect("Failed to find panic message"); + assert_eq!(&msg, expected); +} + +/// Modify body to use a different panic message. +fn change_panic_msg(mut body: Body, new_msg: &str) -> Body { + for bb in &mut body.blocks { + match &mut bb.terminator.kind { + TerminatorKind::Call { args, .. } => { + let new_const = Const::from_str(new_msg); + args[0] = Operand::Constant(Constant { + literal: new_const, + span: bb.terminator.span, + user_ty: None, + }); + } + _ => {} + } + } + body +} + +fn get_item<'a>( + items: &'a CrateItems, + item: (ItemKind, &str), +) -> Option<&'a stable_mir::CrateItem> { + items.iter().find(|crate_item| (item.0 == crate_item.kind()) && crate_item.name() == item.1) +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "transform_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "--crate-type=lib".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_transform).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + #![feature(panic_internals)] + pub fn dummy() {{ + core::panicking::panic_str("oops"); + }} + "# + )?; + Ok(()) +} From dd0f41f003d141efec3404e4b750589b348bd5c6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 12 Mar 2024 16:07:01 -0400 Subject: [PATCH 34/79] Fix WF for AsyncFnKindHelper in new trait solver --- compiler/rustc_middle/src/ty/sty.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index cac12e5ee0bb9..11065b2a382be 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2436,8 +2436,9 @@ impl<'tcx> Ty<'tcx> { }, // "Bound" types appear in canonical queries when the - // closure type is not yet known - Bound(..) | Param(_) | Infer(_) => None, + // closure type is not yet known, and `Placeholder` and `Param` + // may be encountered in generic `AsyncFnKindHelper` goals. + Bound(..) | Placeholder(_) | Param(_) | Infer(_) => None, Error(_) => Some(ty::ClosureKind::Fn), From a38a556ceb4b65c397a47bcbe35d004bc9b8626f Mon Sep 17 00:00:00 2001 From: ltdk Date: Tue, 12 Mar 2024 17:19:58 -0400 Subject: [PATCH 35/79] Reduce unsafe code, use more NonNull APIs per @cuviper review --- library/core/src/ffi/c_str.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 1dafc8c1f868c..30debbffec1aa 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -507,6 +507,13 @@ impl CStr { self.inner.as_ptr() } + /// We could eventually expose this publicly, if we wanted. + #[inline] + #[must_use] + const fn as_non_null_ptr(&self) -> NonNull { + NonNull::from(&self.inner).as_non_null_ptr() + } + /// Returns the length of `self`. Like C's `strlen`, this does not include the nul terminator. /// /// > **Note**: This method is currently implemented as a constant-time @@ -776,12 +783,7 @@ pub struct Bytes<'a> { impl<'a> Bytes<'a> { #[inline] fn new(s: &'a CStr) -> Self { - Self { - // SAFETY: Because we have a valid reference to the string, we know - // that its pointer is non-null. - ptr: unsafe { NonNull::new_unchecked(s.as_ptr() as *const u8 as *mut u8) }, - phantom: PhantomData, - } + Self { ptr: s.as_non_null_ptr().cast(), phantom: PhantomData } } #[inline] @@ -789,7 +791,7 @@ impl<'a> Bytes<'a> { // SAFETY: We uphold that the pointer is always valid to dereference // by starting with a valid C string and then never incrementing beyond // the nul terminator. - unsafe { *self.ptr.as_ref() == 0 } + unsafe { self.ptr.read() == 0 } } } @@ -806,11 +808,11 @@ impl Iterator for Bytes<'_> { // it and assume that adding 1 will create a new, non-null, valid // pointer. unsafe { - let ret = *self.ptr.as_ref(); + let ret = self.ptr.read(); if ret == 0 { None } else { - self.ptr = NonNull::new_unchecked(self.ptr.as_ptr().offset(1)); + self.ptr = self.ptr.offset(1); Some(ret) } } From f2fcfe82af65dd7a2fbc20ca082e1a6794918d0e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 13 Mar 2024 11:53:36 +1100 Subject: [PATCH 36/79] Various style improvements to `rustc_lint::levels` - Replace some nested if-let with let-chains - Tweak a match pattern to allow shorthand struct syntax - Fuse an `is_empty` check with getting the last element - Merge some common code that emits `MalformedAttribute` and continues - Format `"{tool}::{name}"` in a way that's consistent with other match arms - Replace if-let-else-panic with let-else - Use early-exit to flatten a method body --- compiler/rustc_lint/src/levels.rs | 249 +++++++++++++++--------------- 1 file changed, 121 insertions(+), 128 deletions(-) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index e89df1c9840c6..74b6c92dbfed7 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -103,11 +103,12 @@ impl LintLevelSets { mut idx: LintStackIndex, aux: Option<&FxIndexMap>, ) -> (Option, LintLevelSource) { - if let Some(specs) = aux { - if let Some(&(level, src)) = specs.get(&id) { - return (Some(level), src); - } + if let Some(specs) = aux + && let Some(&(level, src)) = specs.get(&id) + { + return (Some(level), src); } + loop { let LintSet { ref specs, parent } = self.list[idx]; if let Some(&(level, src)) = specs.get(&id) { @@ -177,7 +178,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe // There is only something to do if there are attributes at all. [] => {} // Most of the time, there is only one attribute. Avoid fetching HIR in that case. - [(local_id, _)] => levels.add_id(HirId { owner, local_id: *local_id }), + &[(local_id, _)] => levels.add_id(HirId { owner, local_id }), // Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do // a standard visit. // FIXME(#102522) Just iterate on attrs once that iteration order matches HIR's. @@ -643,63 +644,61 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // // This means that this only errors if we're truly lowering the lint // level from forbid. - if self.lint_added_lints && level != Level::Forbid { - if let Level::Forbid = old_level { - // Backwards compatibility check: - // - // We used to not consider `forbid(lint_group)` - // as preventing `allow(lint)` for some lint `lint` in - // `lint_group`. For now, issue a future-compatibility - // warning for this case. - let id_name = id.lint.name_lower(); - let fcw_warning = match old_src { - LintLevelSource::Default => false, - LintLevelSource::Node { name, .. } => self.store.is_lint_group(name), - LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), - }; - debug!( - "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", - fcw_warning, - self.current_specs(), - old_src, - id_name - ); - let sub = match old_src { - LintLevelSource::Default => { - OverruledAttributeSub::DefaultSource { id: id.to_string() } - } - LintLevelSource::Node { span, reason, .. } => { - OverruledAttributeSub::NodeSource { span, reason } - } - LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource, - }; - if !fcw_warning { - self.sess.dcx().emit_err(OverruledAttribute { - span: src.span(), + if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid { + // Backwards compatibility check: + // + // We used to not consider `forbid(lint_group)` + // as preventing `allow(lint)` for some lint `lint` in + // `lint_group`. For now, issue a future-compatibility + // warning for this case. + let id_name = id.lint.name_lower(); + let fcw_warning = match old_src { + LintLevelSource::Default => false, + LintLevelSource::Node { name, .. } => self.store.is_lint_group(name), + LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), + }; + debug!( + "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", + fcw_warning, + self.current_specs(), + old_src, + id_name + ); + let sub = match old_src { + LintLevelSource::Default => { + OverruledAttributeSub::DefaultSource { id: id.to_string() } + } + LintLevelSource::Node { span, reason, .. } => { + OverruledAttributeSub::NodeSource { span, reason } + } + LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource, + }; + if !fcw_warning { + self.sess.dcx().emit_err(OverruledAttribute { + span: src.span(), + overruled: src.span(), + lint_level: level.as_str(), + lint_source: src.name(), + sub, + }); + } else { + self.emit_span_lint( + FORBIDDEN_LINT_GROUPS, + src.span().into(), + OverruledAttributeLint { overruled: src.span(), lint_level: level.as_str(), lint_source: src.name(), sub, - }); - } else { - self.emit_span_lint( - FORBIDDEN_LINT_GROUPS, - src.span().into(), - OverruledAttributeLint { - overruled: src.span(), - lint_level: level.as_str(), - lint_source: src.name(), - sub, - }, - ); - } + }, + ); + } - // Retain the forbid lint level, unless we are - // issuing a FCW. In the FCW case, we want to - // respect the new setting. - if !fcw_warning { - return; - } + // Retain the forbid lint level, unless we are + // issuing a FCW. In the FCW case, we want to + // respect the new setting. + if !fcw_warning { + return; } } @@ -770,15 +769,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let Some(mut metas) = attr.meta_item_list() else { continue }; - if metas.is_empty() { + // Check whether `metas` is empty, and get its last element. + let Some(tail_li) = metas.last() else { // This emits the unused_attributes lint for `#[level()]` continue; - } + }; // Before processing the lint names, look for a reason (RFC 2383) // at the end. let mut reason = None; - let tail_li = &metas[metas.len() - 1]; if let Some(item) = tail_li.meta_item() { match item.kind { ast::MetaItemKind::Word => {} // actual lint names handled later @@ -834,21 +833,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let meta_item = match li { ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item, _ => { - if let Some(item) = li.meta_item() { - if let ast::MetaItemKind::NameValue(_) = item.kind { - if item.path == sym::reason { - sess.dcx().emit_err(MalformedAttribute { - span: sp, - sub: MalformedAttributeSub::ReasonMustComeLast(sp), - }); - continue; - } - } - } - sess.dcx().emit_err(MalformedAttribute { - span: sp, - sub: MalformedAttributeSub::BadAttributeArgument(sp), - }); + let sub = if let Some(item) = li.meta_item() + && let ast::MetaItemKind::NameValue(_) = item.kind + && item.path == sym::reason + { + MalformedAttributeSub::ReasonMustComeLast(sp) + } else { + MalformedAttributeSub::BadAttributeArgument(sp) + }; + + sess.dcx().emit_err(MalformedAttribute { span: sp, sub }); continue; } }; @@ -987,11 +981,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } CheckLintNameResult::NoLint(suggestion) => { - let name = if let Some(tool_ident) = tool_ident { - format!("{}::{}", tool_ident.name, name) - } else { - name.to_string() - }; + let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); let suggestion = suggestion.map(|(replace, from_rustc)| { UnknownLintSuggestion::WithSpan { suggestion: sp, replace, from_rustc } }); @@ -1005,27 +995,24 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if let CheckLintNameResult::Renamed(new_name) = lint_result { // Ignore any errors or warnings that happen because the new name is inaccurate // NOTE: `new_name` already includes the tool name, so we don't have to add it again. - if let CheckLintNameResult::Ok(ids) = + let CheckLintNameResult::Ok(ids) = self.store.check_lint_name(&new_name, None, self.registered_tools) - { - let src = LintLevelSource::Node { - name: Symbol::intern(&new_name), - span: sp, - reason, - }; - for &id in ids { - if self.check_gated_lint(id, attr.span, false) { - self.insert_spec(id, (level, src)); - } - } - if let Level::Expect(expect_id) = level { - self.provider.push_expectation( - expect_id, - LintExpectation::new(reason, sp, false, tool_name), - ); - } - } else { + else { panic!("renamed lint does not exist: {new_name}"); + }; + + let src = + LintLevelSource::Node { name: Symbol::intern(&new_name), span: sp, reason }; + for &id in ids { + if self.check_gated_lint(id, attr.span, false) { + self.insert_spec(id, (level, src)); + } + } + if let Level::Expect(expect_id) = level { + self.provider.push_expectation( + expect_id, + LintExpectation::new(reason, sp, false, tool_name), + ); } } } @@ -1058,38 +1045,44 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// Returns `true` if the lint's feature is enabled. #[track_caller] fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool { - if let Some(feature) = lint_id.lint.feature_gate { - if !self.features.active(feature) { - if self.lint_added_lints { - let lint = builtin::UNKNOWN_LINTS; - let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS); - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] - lint_level( - self.sess, + let feature = if let Some(feature) = lint_id.lint.feature_gate + && !self.features.active(feature) + { + // Lint is behind a feature that is not enabled; eventually return false. + feature + } else { + // Lint is ungated or its feature is enabled; exit early. + return true; + }; + + if self.lint_added_lints { + let lint = builtin::UNKNOWN_LINTS; + let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS); + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] + lint_level( + self.sess, + lint, + level, + src, + Some(span.into()), + fluent::lint_unknown_gated_lint, + |lint| { + lint.arg("name", lint_id.lint.name_lower()); + lint.note(fluent::lint_note); + rustc_session::parse::add_feature_diagnostics_for_issue( lint, - level, - src, - Some(span.into()), - fluent::lint_unknown_gated_lint, - |lint| { - lint.arg("name", lint_id.lint.name_lower()); - lint.note(fluent::lint_note); - rustc_session::parse::add_feature_diagnostics_for_issue( - lint, - &self.sess, - feature, - GateIssue::Language, - lint_from_cli, - ); - }, + &self.sess, + feature, + GateIssue::Language, + lint_from_cli, ); - } - return false; - } + }, + ); } - true + + false } /// Find the lint level for a lint. From c527ec76ce410c67a5da38f738f23a90e890dca6 Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Wed, 13 Mar 2024 00:49:51 -0400 Subject: [PATCH 37/79] Improve Step docs --- library/core/src/iter/range.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 68937161e046a..2ecf9ce5b81b3 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -22,7 +22,7 @@ unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u6 /// /// The *successor* operation moves towards values that compare greater. /// The *predecessor* operation moves towards values that compare lesser. -#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +#[unstable(feature = "step_trait", issue = "42168")] pub trait Step: Clone + PartialOrd + Sized { /// Returns the number of *successor* steps required to get from `start` to `end`. /// @@ -52,15 +52,12 @@ pub trait Step: Clone + PartialOrd + Sized { /// For any `a`, `n`, and `m`: /// /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))` - /// - /// For any `a`, `n`, and `m` where `n + m` does not overflow: - /// - /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + m)` + /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == try { Step::forward_checked(a, n.checked_add(m)) }` /// /// For any `a` and `n`: /// /// * `Step::forward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::forward_checked(&x, 1))` - /// * Corollary: `Step::forward_checked(&a, 0) == Some(a)` + /// * Corollary: `Step::forward_checked(a, 0) == Some(a)` fn forward_checked(start: Self, count: usize) -> Option; /// Returns the value that would be obtained by taking the *successor* @@ -106,6 +103,7 @@ pub trait Step: Clone + PartialOrd + Sized { /// * if there exists `b` such that `b > a`, it is safe to call `Step::forward_unchecked(a, 1)` /// * if there exists `b`, `n` such that `steps_between(&a, &b) == Some(n)`, /// it is safe to call `Step::forward_unchecked(a, m)` for any `m <= n`. + /// * Corollary: `Step::forward_unchecked(a, 0)` is always safe. /// /// For any `a` and `n`, where no overflow occurs: /// @@ -128,8 +126,8 @@ pub trait Step: Clone + PartialOrd + Sized { /// /// For any `a` and `n`: /// - /// * `Step::backward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::backward_checked(&x, 1))` - /// * Corollary: `Step::backward_checked(&a, 0) == Some(a)` + /// * `Step::backward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::backward_checked(x, 1))` + /// * Corollary: `Step::backward_checked(a, 0) == Some(a)` fn backward_checked(start: Self, count: usize) -> Option; /// Returns the value that would be obtained by taking the *predecessor* @@ -175,6 +173,7 @@ pub trait Step: Clone + PartialOrd + Sized { /// * if there exists `b` such that `b < a`, it is safe to call `Step::backward_unchecked(a, 1)` /// * if there exists `b`, `n` such that `steps_between(&b, &a) == Some(n)`, /// it is safe to call `Step::backward_unchecked(a, m)` for any `m <= n`. + /// * Corollary: `Step::backward_unchecked(a, 0)` is always safe. /// /// For any `a` and `n`, where no overflow occurs: /// From 89fab06a772985cad3e117f52a7f5fdb12804fc1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 25 Dec 2023 21:23:15 +1100 Subject: [PATCH 38/79] coverage: Add branch coverage tests (with branch coverage disabled) --- tests/coverage/branch_generics.cov-map | 45 ++++++++ tests/coverage/branch_generics.coverage | 48 +++++++++ tests/coverage/branch_generics.rs | 19 ++++ tests/coverage/branch_guard.cov-map | 24 +++++ tests/coverage/branch_guard.coverage | 39 +++++++ tests/coverage/branch_guard.rs | 37 +++++++ tests/coverage/branch_if.cov-map | 136 ++++++++++++++++++++++++ tests/coverage/branch_if.coverage | 86 +++++++++++++++ tests/coverage/branch_if.rs | 81 ++++++++++++++ tests/coverage/branch_while.cov-map | 74 +++++++++++++ tests/coverage/branch_while.coverage | 60 +++++++++++ tests/coverage/branch_while.rs | 58 ++++++++++ 12 files changed, 707 insertions(+) create mode 100644 tests/coverage/branch_generics.cov-map create mode 100644 tests/coverage/branch_generics.coverage create mode 100644 tests/coverage/branch_generics.rs create mode 100644 tests/coverage/branch_guard.cov-map create mode 100644 tests/coverage/branch_guard.coverage create mode 100644 tests/coverage/branch_guard.rs create mode 100644 tests/coverage/branch_if.cov-map create mode 100644 tests/coverage/branch_if.coverage create mode 100644 tests/coverage/branch_if.rs create mode 100644 tests/coverage/branch_while.cov-map create mode 100644 tests/coverage/branch_while.coverage create mode 100644 tests/coverage/branch_while.rs diff --git a/tests/coverage/branch_generics.cov-map b/tests/coverage/branch_generics.cov-map new file mode 100644 index 0000000000000..ff8bb632a54f6 --- /dev/null +++ b/tests/coverage/branch_generics.cov-map @@ -0,0 +1,45 @@ +Function name: branch_generics::print_size::<()> +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 06, 01, 01, 24, 05, 01, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) +- Code(Counter(1)) at (prev + 1, 37) to (start + 2, 6) +- Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + +Function name: branch_generics::print_size:: +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 06, 01, 01, 24, 05, 01, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) +- Code(Counter(1)) at (prev + 1, 37) to (start + 2, 6) +- Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + +Function name: branch_generics::print_size:: +Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 06, 01, 01, 24, 05, 01, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) +- Code(Counter(1)) at (prev + 1, 37) to (start + 2, 6) +- Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + diff --git a/tests/coverage/branch_generics.coverage b/tests/coverage/branch_generics.coverage new file mode 100644 index 0000000000000..cfbd2d3f4bd46 --- /dev/null +++ b/tests/coverage/branch_generics.coverage @@ -0,0 +1,48 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| | + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| 3|fn print_size() { + LL| 3| if std::mem::size_of::() > 4 { + LL| 1| println!("size > 4"); + LL| 2| } else { + LL| 2| println!("size <= 4"); + LL| 2| } + LL| 3|} + ------------------ + | branch_generics::print_size::<()>: + | LL| 1|fn print_size() { + | LL| 1| if std::mem::size_of::() > 4 { + | LL| 0| println!("size > 4"); + | LL| 1| } else { + | LL| 1| println!("size <= 4"); + | LL| 1| } + | LL| 1|} + ------------------ + | branch_generics::print_size::: + | LL| 1|fn print_size() { + | LL| 1| if std::mem::size_of::() > 4 { + | LL| 0| println!("size > 4"); + | LL| 1| } else { + | LL| 1| println!("size <= 4"); + | LL| 1| } + | LL| 1|} + ------------------ + | branch_generics::print_size::: + | LL| 1|fn print_size() { + | LL| 1| if std::mem::size_of::() > 4 { + | LL| 1| println!("size > 4"); + | LL| 1| } else { + | LL| 0| println!("size <= 4"); + | LL| 0| } + | LL| 1|} + ------------------ + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | print_size::<()>(); + LL| | print_size::(); + LL| | print_size::(); + LL| |} + diff --git a/tests/coverage/branch_generics.rs b/tests/coverage/branch_generics.rs new file mode 100644 index 0000000000000..ad1f5be33c4b1 --- /dev/null +++ b/tests/coverage/branch_generics.rs @@ -0,0 +1,19 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 + +//@ llvm-cov-flags: --show-branches=count + +fn print_size() { + if std::mem::size_of::() > 4 { + println!("size > 4"); + } else { + println!("size <= 4"); + } +} + +#[coverage(off)] +fn main() { + print_size::<()>(); + print_size::(); + print_size::(); +} diff --git a/tests/coverage/branch_guard.cov-map b/tests/coverage/branch_guard.cov-map new file mode 100644 index 0000000000000..e0cbbf491960f --- /dev/null +++ b/tests/coverage/branch_guard.cov-map @@ -0,0 +1,24 @@ +Function name: branch_guard::branch_match_guard +Raw bytes (67): 0x[01, 01, 04, 05, 09, 0b, 15, 0f, 11, 03, 0d, 0b, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 15, 01, 14, 02, 0a, 0d, 03, 0e, 00, 0f, 19, 00, 14, 00, 19, 0d, 00, 1d, 02, 0a, 11, 03, 0e, 00, 0f, 1d, 00, 14, 00, 19, 11, 00, 1d, 02, 0a, 03, 03, 0e, 02, 0a, 07, 04, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(5) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(4) +- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(3) +Number of file 0 mappings: 11 +- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) +- Code(Counter(7)) at (prev + 3, 11) to (start + 0, 12) +- Code(Counter(5)) at (prev + 1, 20) to (start + 2, 10) +- Code(Counter(3)) at (prev + 3, 14) to (start + 0, 15) +- Code(Counter(6)) at (prev + 0, 20) to (start + 0, 25) +- Code(Counter(3)) at (prev + 0, 29) to (start + 2, 10) +- Code(Counter(4)) at (prev + 3, 14) to (start + 0, 15) +- Code(Counter(7)) at (prev + 0, 20) to (start + 0, 25) +- Code(Counter(4)) at (prev + 0, 29) to (start + 2, 10) +- Code(Expression(0, Add)) at (prev + 3, 14) to (start + 2, 10) + = (c1 + c2) +- Code(Expression(1, Add)) at (prev + 4, 1) to (start + 0, 2) + = ((((c1 + c2) + c3) + c4) + c5) + diff --git a/tests/coverage/branch_guard.coverage b/tests/coverage/branch_guard.coverage new file mode 100644 index 0000000000000..6156ae88c7461 --- /dev/null +++ b/tests/coverage/branch_guard.coverage @@ -0,0 +1,39 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| | + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 4|fn branch_match_guard(x: Option) { + LL| 4| no_merge!(); + LL| | + LL| 1| match x { + LL| 1| Some(0) => { + LL| 1| println!("zero"); + LL| 1| } + LL| 3| Some(x) if x % 2 == 0 => { + ^2 + LL| 2| println!("is nonzero and even"); + LL| 2| } + LL| 1| Some(x) if x % 3 == 0 => { + LL| 1| println!("is nonzero and odd, but divisible by 3"); + LL| 1| } + LL| 0| _ => { + LL| 0| println!("something else"); + LL| 0| } + LL| | } + LL| 4|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | branch_match_guard(Some(0)); + LL| | branch_match_guard(Some(2)); + LL| | branch_match_guard(Some(6)); + LL| | branch_match_guard(Some(3)); + LL| |} + diff --git a/tests/coverage/branch_guard.rs b/tests/coverage/branch_guard.rs new file mode 100644 index 0000000000000..a7cb389227e81 --- /dev/null +++ b/tests/coverage/branch_guard.rs @@ -0,0 +1,37 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 + +//@ llvm-cov-flags: --show-branches=count + +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn branch_match_guard(x: Option) { + no_merge!(); + + match x { + Some(0) => { + println!("zero"); + } + Some(x) if x % 2 == 0 => { + println!("is nonzero and even"); + } + Some(x) if x % 3 == 0 => { + println!("is nonzero and odd, but divisible by 3"); + } + _ => { + println!("something else"); + } + } +} + +#[coverage(off)] +fn main() { + branch_match_guard(Some(0)); + branch_match_guard(Some(2)); + branch_match_guard(Some(6)); + branch_match_guard(Some(3)); +} diff --git a/tests/coverage/branch_if.cov-map b/tests/coverage/branch_if.cov-map new file mode 100644 index 0000000000000..6fb5ef767148f --- /dev/null +++ b/tests/coverage/branch_if.cov-map @@ -0,0 +1,136 @@ +Function name: branch_if::branch_and +Raw bytes (40): 0x[01, 01, 03, 06, 0d, 05, 09, 11, 03, 06, 01, 2b, 01, 01, 10, 05, 03, 08, 00, 09, 09, 00, 0d, 00, 0e, 11, 00, 0f, 02, 06, 03, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Expression(1, Sub), rhs = Counter(3) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Counter(4), rhs = Expression(0, Add) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 43, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) +- Code(Counter(2)) at (prev + 0, 13) to (start + 0, 14) +- Code(Counter(4)) at (prev + 0, 15) to (start + 2, 6) +- Code(Expression(0, Add)) at (prev + 2, 12) to (start + 2, 6) + = ((c1 - c2) + c3) +- Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2) + = (c4 + ((c1 - c2) + c3)) + +Function name: branch_if::branch_not +Raw bytes (132): 0x[01, 01, 1d, 05, 09, 09, 02, 73, 0d, 09, 02, 0d, 6e, 73, 0d, 09, 02, 6b, 11, 0d, 6e, 73, 0d, 09, 02, 11, 66, 6b, 11, 0d, 6e, 73, 0d, 09, 02, 63, 15, 11, 66, 6b, 11, 0d, 6e, 73, 0d, 09, 02, 15, 5e, 63, 15, 11, 66, 6b, 11, 0d, 6e, 73, 0d, 09, 02, 0e, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, 73, 01, 08, 00, 0a, 6e, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 6b, 01, 08, 00, 0b, 11, 00, 0c, 02, 06, 66, 02, 06, 00, 07, 63, 01, 08, 00, 0c, 5e, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 5b, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 29 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 2 operands: lhs = Expression(28, Add), rhs = Counter(3) +- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 4 operands: lhs = Counter(3), rhs = Expression(27, Sub) +- expression 5 operands: lhs = Expression(28, Add), rhs = Counter(3) +- expression 6 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 7 operands: lhs = Expression(26, Add), rhs = Counter(4) +- expression 8 operands: lhs = Counter(3), rhs = Expression(27, Sub) +- expression 9 operands: lhs = Expression(28, Add), rhs = Counter(3) +- expression 10 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 11 operands: lhs = Counter(4), rhs = Expression(25, Sub) +- expression 12 operands: lhs = Expression(26, Add), rhs = Counter(4) +- expression 13 operands: lhs = Counter(3), rhs = Expression(27, Sub) +- expression 14 operands: lhs = Expression(28, Add), rhs = Counter(3) +- expression 15 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 16 operands: lhs = Expression(24, Add), rhs = Counter(5) +- expression 17 operands: lhs = Counter(4), rhs = Expression(25, Sub) +- expression 18 operands: lhs = Expression(26, Add), rhs = Counter(4) +- expression 19 operands: lhs = Counter(3), rhs = Expression(27, Sub) +- expression 20 operands: lhs = Expression(28, Add), rhs = Counter(3) +- expression 21 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 22 operands: lhs = Counter(5), rhs = Expression(23, Sub) +- expression 23 operands: lhs = Expression(24, Add), rhs = Counter(5) +- expression 24 operands: lhs = Counter(4), rhs = Expression(25, Sub) +- expression 25 operands: lhs = Expression(26, Add), rhs = Counter(4) +- expression 26 operands: lhs = Counter(3), rhs = Expression(27, Sub) +- expression 27 operands: lhs = Expression(28, Add), rhs = Counter(3) +- expression 28 operands: lhs = Counter(2), rhs = Expression(0, Sub) +Number of file 0 mappings: 14 +- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) +- Code(Counter(2)) at (prev + 1, 9) to (start + 0, 17) +- Code(Expression(0, Sub)) at (prev + 1, 6) to (start + 0, 7) + = (c1 - c2) +- Code(Expression(28, Add)) at (prev + 1, 8) to (start + 0, 10) + = (c2 + (c1 - c2)) +- Code(Expression(27, Sub)) at (prev + 0, 11) to (start + 2, 6) + = ((c2 + (c1 - c2)) - c3) +- Code(Counter(3)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(26, Add)) at (prev + 1, 8) to (start + 0, 11) + = (c3 + ((c2 + (c1 - c2)) - c3)) +- Code(Counter(4)) at (prev + 0, 12) to (start + 2, 6) +- Code(Expression(25, Sub)) at (prev + 2, 6) to (start + 0, 7) + = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) +- Code(Expression(24, Add)) at (prev + 1, 8) to (start + 0, 12) + = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) +- Code(Expression(23, Sub)) at (prev + 0, 13) to (start + 2, 6) + = ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5) +- Code(Counter(5)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(22, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c5 + ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5)) + +Function name: branch_if::branch_not_as +Raw bytes (91): 0x[01, 01, 10, 05, 09, 09, 02, 3f, 0d, 09, 02, 0d, 3a, 3f, 0d, 09, 02, 37, 11, 0d, 3a, 3f, 0d, 09, 02, 11, 32, 37, 11, 0d, 3a, 3f, 0d, 09, 02, 0b, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 06, 00, 07, 3f, 01, 08, 00, 15, 0d, 00, 16, 02, 06, 3a, 02, 06, 00, 07, 37, 01, 08, 00, 16, 32, 00, 17, 02, 06, 11, 02, 06, 00, 07, 2f, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 16 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 2 operands: lhs = Expression(15, Add), rhs = Counter(3) +- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 4 operands: lhs = Counter(3), rhs = Expression(14, Sub) +- expression 5 operands: lhs = Expression(15, Add), rhs = Counter(3) +- expression 6 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 7 operands: lhs = Expression(13, Add), rhs = Counter(4) +- expression 8 operands: lhs = Counter(3), rhs = Expression(14, Sub) +- expression 9 operands: lhs = Expression(15, Add), rhs = Counter(3) +- expression 10 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 11 operands: lhs = Counter(4), rhs = Expression(12, Sub) +- expression 12 operands: lhs = Expression(13, Add), rhs = Counter(4) +- expression 13 operands: lhs = Counter(3), rhs = Expression(14, Sub) +- expression 14 operands: lhs = Expression(15, Add), rhs = Counter(3) +- expression 15 operands: lhs = Counter(2), rhs = Expression(0, Sub) +Number of file 0 mappings: 11 +- Code(Counter(0)) at (prev + 29, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 8) to (start + 0, 20) +- Code(Expression(0, Sub)) at (prev + 0, 21) to (start + 2, 6) + = (c1 - c2) +- Code(Counter(2)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(15, Add)) at (prev + 1, 8) to (start + 0, 21) + = (c2 + (c1 - c2)) +- Code(Counter(3)) at (prev + 0, 22) to (start + 2, 6) +- Code(Expression(14, Sub)) at (prev + 2, 6) to (start + 0, 7) + = ((c2 + (c1 - c2)) - c3) +- Code(Expression(13, Add)) at (prev + 1, 8) to (start + 0, 22) + = (c3 + ((c2 + (c1 - c2)) - c3)) +- Code(Expression(12, Sub)) at (prev + 0, 23) to (start + 2, 6) + = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) +- Code(Counter(4)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(11, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) + +Function name: branch_if::branch_or +Raw bytes (42): 0x[01, 01, 04, 05, 09, 09, 0d, 0f, 11, 09, 0d, 06, 01, 35, 01, 01, 10, 05, 03, 08, 00, 09, 02, 00, 0d, 00, 0e, 0f, 00, 0f, 02, 06, 11, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(4) +- expression 3 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 53, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) +- Code(Expression(0, Sub)) at (prev + 0, 13) to (start + 0, 14) + = (c1 - c2) +- Code(Expression(3, Add)) at (prev + 0, 15) to (start + 2, 6) + = (c2 + c3) +- Code(Counter(4)) at (prev + 2, 12) to (start + 2, 6) +- Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2) + = ((c2 + c3) + c4) + diff --git a/tests/coverage/branch_if.coverage b/tests/coverage/branch_if.coverage new file mode 100644 index 0000000000000..babefb51d3f21 --- /dev/null +++ b/tests/coverage/branch_if.coverage @@ -0,0 +1,86 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| | + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 3|fn branch_not(a: bool) { + LL| 3| no_merge!(); + LL| | + LL| 3| if a { + LL| 2| say("a") + LL| 1| } + LL| 3| if !a { + LL| 1| say("not a"); + LL| 2| } + LL| 3| if !!a { + LL| 2| say("not not a"); + LL| 2| } + ^1 + LL| 3| if !!!a { + LL| 1| say("not not not a"); + LL| 2| } + LL| 3|} + LL| | + LL| 3|fn branch_not_as(a: bool) { + LL| 3| no_merge!(); + LL| | + LL| 3| if !(a as bool) { + LL| 1| say("not (a as bool)"); + LL| 2| } + LL| 3| if !!(a as bool) { + LL| 2| say("not not (a as bool)"); + LL| 2| } + ^1 + LL| 3| if !!!(a as bool) { + LL| 1| say("not not (a as bool)"); + LL| 2| } + LL| 3|} + LL| | + LL| 15|fn branch_and(a: bool, b: bool) { + LL| 15| no_merge!(); + LL| | + LL| 15| if a && b { + ^12 + LL| 8| say("both"); + LL| 8| } else { + LL| 7| say("not both"); + LL| 7| } + LL| 15|} + LL| | + LL| 15|fn branch_or(a: bool, b: bool) { + LL| 15| no_merge!(); + LL| | + LL| 15| if a || b { + ^3 + LL| 14| say("either"); + LL| 14| } else { + LL| 1| say("neither"); + LL| 1| } + LL| 15|} + LL| | + LL| |#[coverage(off)] + LL| |fn say(message: &str) { + LL| | core::hint::black_box(message); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | for a in [false, true, true] { + LL| | branch_not(a); + LL| | branch_not_as(a); + LL| | } + LL| | + LL| | for a in [false, true, true, true, true] { + LL| | for b in [false, true, true] { + LL| | branch_and(a, b); + LL| | branch_or(a, b); + LL| | } + LL| | } + LL| |} + diff --git a/tests/coverage/branch_if.rs b/tests/coverage/branch_if.rs new file mode 100644 index 0000000000000..55ef159ebdfc5 --- /dev/null +++ b/tests/coverage/branch_if.rs @@ -0,0 +1,81 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 + +//@ llvm-cov-flags: --show-branches=count + +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn branch_not(a: bool) { + no_merge!(); + + if a { + say("a") + } + if !a { + say("not a"); + } + if !!a { + say("not not a"); + } + if !!!a { + say("not not not a"); + } +} + +fn branch_not_as(a: bool) { + no_merge!(); + + if !(a as bool) { + say("not (a as bool)"); + } + if !!(a as bool) { + say("not not (a as bool)"); + } + if !!!(a as bool) { + say("not not (a as bool)"); + } +} + +fn branch_and(a: bool, b: bool) { + no_merge!(); + + if a && b { + say("both"); + } else { + say("not both"); + } +} + +fn branch_or(a: bool, b: bool) { + no_merge!(); + + if a || b { + say("either"); + } else { + say("neither"); + } +} + +#[coverage(off)] +fn say(message: &str) { + core::hint::black_box(message); +} + +#[coverage(off)] +fn main() { + for a in [false, true, true] { + branch_not(a); + branch_not_as(a); + } + + for a in [false, true, true, true, true] { + for b in [false, true, true] { + branch_and(a, b); + branch_or(a, b); + } + } +} diff --git a/tests/coverage/branch_while.cov-map b/tests/coverage/branch_while.cov-map new file mode 100644 index 0000000000000..63a7c438163ea --- /dev/null +++ b/tests/coverage/branch_while.cov-map @@ -0,0 +1,74 @@ +Function name: branch_while::while_cond +Raw bytes (33): 0x[01, 01, 02, 05, 09, 03, 09, 05, 01, 0c, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 10, 09, 00, 11, 02, 06, 06, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 9) to (start + 0, 18) +- Code(Expression(0, Add)) at (prev + 1, 11) to (start + 0, 16) + = (c1 + c2) +- Code(Counter(2)) at (prev + 0, 17) to (start + 2, 6) +- Code(Expression(1, Sub)) at (prev + 3, 1) to (start + 0, 2) + = ((c1 + c2) - c2) + +Function name: branch_while::while_cond_not +Raw bytes (33): 0x[01, 01, 02, 05, 09, 03, 09, 05, 01, 15, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 14, 09, 00, 15, 02, 06, 06, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 21, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 9) to (start + 0, 18) +- Code(Expression(0, Add)) at (prev + 1, 11) to (start + 0, 20) + = (c1 + c2) +- Code(Counter(2)) at (prev + 0, 21) to (start + 2, 6) +- Code(Expression(1, Sub)) at (prev + 3, 1) to (start + 0, 2) + = ((c1 + c2) - c2) + +Function name: branch_while::while_op_and +Raw bytes (40): 0x[01, 01, 03, 05, 09, 03, 0d, 11, 0d, 06, 01, 1e, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 06, 00, 14, 00, 19, 09, 00, 1a, 03, 06, 0b, 04, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 2 operands: lhs = Counter(4), rhs = Counter(3) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 30, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 9) to (start + 1, 18) +- Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 16) + = (c1 + c2) +- Code(Expression(1, Sub)) at (prev + 0, 20) to (start + 0, 25) + = ((c1 + c2) - c3) +- Code(Counter(2)) at (prev + 0, 26) to (start + 3, 6) +- Code(Expression(2, Add)) at (prev + 4, 1) to (start + 0, 2) + = (c4 + c3) + +Function name: branch_while::while_op_or +Raw bytes (46): 0x[01, 01, 06, 05, 0f, 09, 0d, 03, 09, 09, 0d, 16, 0d, 03, 09, 06, 01, 29, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 16, 00, 14, 00, 19, 0f, 00, 1a, 03, 06, 12, 04, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 6 +- expression 0 operands: lhs = Counter(1), rhs = Expression(3, Add) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(2) +- expression 3 operands: lhs = Counter(2), rhs = Counter(3) +- expression 4 operands: lhs = Expression(5, Sub), rhs = Counter(3) +- expression 5 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 41, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 9) to (start + 1, 18) +- Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 16) + = (c1 + (c2 + c3)) +- Code(Expression(5, Sub)) at (prev + 0, 20) to (start + 0, 25) + = ((c1 + (c2 + c3)) - c2) +- Code(Expression(3, Add)) at (prev + 0, 26) to (start + 3, 6) + = (c2 + c3) +- Code(Expression(4, Sub)) at (prev + 4, 1) to (start + 0, 2) + = (((c1 + (c2 + c3)) - c2) - c3) + diff --git a/tests/coverage/branch_while.coverage b/tests/coverage/branch_while.coverage new file mode 100644 index 0000000000000..d2351a7de09a2 --- /dev/null +++ b/tests/coverage/branch_while.coverage @@ -0,0 +1,60 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| | + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 1|fn while_cond() { + LL| 1| no_merge!(); + LL| | + LL| 1| let mut a = 8; + LL| 9| while a > 0 { + LL| 8| a -= 1; + LL| 8| } + LL| 1|} + LL| | + LL| 1|fn while_cond_not() { + LL| 1| no_merge!(); + LL| | + LL| 1| let mut a = 8; + LL| 9| while !(a == 0) { + LL| 8| a -= 1; + LL| 8| } + LL| 1|} + LL| | + LL| 1|fn while_op_and() { + LL| 1| no_merge!(); + LL| | + LL| 1| let mut a = 8; + LL| 1| let mut b = 4; + LL| 5| while a > 0 && b > 0 { + LL| 4| a -= 1; + LL| 4| b -= 1; + LL| 4| } + LL| 1|} + LL| | + LL| 1|fn while_op_or() { + LL| 1| no_merge!(); + LL| | + LL| 1| let mut a = 4; + LL| 1| let mut b = 8; + LL| 9| while a > 0 || b > 0 { + ^5 + LL| 8| a -= 1; + LL| 8| b -= 1; + LL| 8| } + LL| 1|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | while_cond(); + LL| | while_cond_not(); + LL| | while_op_and(); + LL| | while_op_or(); + LL| |} + diff --git a/tests/coverage/branch_while.rs b/tests/coverage/branch_while.rs new file mode 100644 index 0000000000000..99e9a798eff5f --- /dev/null +++ b/tests/coverage/branch_while.rs @@ -0,0 +1,58 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 + +//@ llvm-cov-flags: --show-branches=count + +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn while_cond() { + no_merge!(); + + let mut a = 8; + while a > 0 { + a -= 1; + } +} + +fn while_cond_not() { + no_merge!(); + + let mut a = 8; + while !(a == 0) { + a -= 1; + } +} + +fn while_op_and() { + no_merge!(); + + let mut a = 8; + let mut b = 4; + while a > 0 && b > 0 { + a -= 1; + b -= 1; + } +} + +fn while_op_or() { + no_merge!(); + + let mut a = 4; + let mut b = 8; + while a > 0 || b > 0 { + a -= 1; + b -= 1; + } +} + +#[coverage(off)] +fn main() { + while_cond(); + while_cond_not(); + while_op_and(); + while_op_or(); +} From 975109892cf8887223b37d066e23bb766b04f79e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 8 Feb 2024 11:46:26 +1100 Subject: [PATCH 39/79] Allow `rustc_mir_transform` to register hook providers --- compiler/rustc_mir_transform/src/coverage/query.rs | 6 +++--- compiler/rustc_mir_transform/src/lib.rs | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index dfc7c3a713bc2..1354273e2c488 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -3,12 +3,12 @@ use super::*; use rustc_data_structures::captures::Captures; use rustc_middle::mir::coverage::*; use rustc_middle::mir::{Body, CoverageIdsInfo}; -use rustc_middle::query::Providers; use rustc_middle::ty::{self}; +use rustc_middle::util::Providers; -/// A `query` provider for retrieving coverage information injected into MIR. +/// Registers query/hook implementations related to coverage. pub(crate) fn provide(providers: &mut Providers) { - providers.coverage_ids_info = |tcx, def_id| coverage_ids_info(tcx, def_id); + providers.queries.coverage_ids_info = coverage_ids_info; } /// Query implementation for `coverage_ids_info`. diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 0491de7826590..4551380152217 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -37,8 +37,9 @@ use rustc_middle::mir::{ LocalDecl, MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK, }; -use rustc_middle::query::Providers; +use rustc_middle::query; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_middle::util::Providers; use rustc_span::{source_map::Spanned, sym, DUMMY_SP}; use rustc_trait_selection::traits; @@ -124,7 +125,7 @@ pub fn provide(providers: &mut Providers) { ffi_unwind_calls::provide(providers); shim::provide(providers); cross_crate_inline::provide(providers); - *providers = Providers { + providers.queries = query::Providers { mir_keys, mir_const, mir_const_qualif, @@ -139,7 +140,7 @@ pub fn provide(providers: &mut Providers) { mir_inliner_callees: inline::cycle::mir_inliner_callees, promoted_mir, deduced_param_attrs: deduce_param_attrs::deduced_param_attrs, - ..*providers + ..providers.queries }; } From 73475d0d597dfb42a0d2071b4369f5c3dc7281b1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 8 Feb 2024 12:21:22 +1100 Subject: [PATCH 40/79] coverage: Make `is_eligible_for_coverage` a hook method This will allow MIR building to check whether a function is eligible for coverage instrumentation, and avoid collecting branch coverage info if it is not. --- compiler/rustc_middle/src/hooks/mod.rs | 7 +++ .../rustc_mir_transform/src/coverage/mod.rs | 34 +------------- .../rustc_mir_transform/src/coverage/query.rs | 45 ++++++++++++++++--- 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 8588ae2033613..b984df3646e61 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -6,6 +6,7 @@ use crate::mir; use crate::query::TyCtxtAt; use crate::ty::{Ty, TyCtxt}; +use rustc_span::def_id::LocalDefId; use rustc_span::DUMMY_SP; macro_rules! declare_hooks { @@ -70,4 +71,10 @@ declare_hooks! { /// Getting a &core::panic::Location referring to a span. hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>; + + /// Returns `true` if this def is a function-like thing that is eligible for + /// coverage instrumentation under `-Cinstrument-coverage`. + /// + /// (Eligible functions might nevertheless be skipped for other reasons.) + hook is_eligible_for_coverage(key: LocalDefId) -> bool; } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 4c5be0a3f4bed..bde135834860b 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -14,7 +14,6 @@ use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans}; use crate::MirPass; use rustc_middle::hir; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::*; use rustc_middle::mir::{ self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator, @@ -44,7 +43,7 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage { let def_id = mir_source.def_id().expect_local(); - if !is_eligible_for_coverage(tcx, def_id) { + if !tcx.is_eligible_for_coverage(def_id) { trace!("InstrumentCoverage skipped for {def_id:?} (not eligible)"); return; } @@ -349,37 +348,6 @@ fn check_code_region(code_region: CodeRegion) -> Option { } } -fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - // Only instrument functions, methods, and closures (not constants since they are evaluated - // at compile time by Miri). - // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const - // expressions get coverage spans, we will probably have to "carve out" space for const - // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might - // be tricky if const expressions have no corresponding statements in the enclosing MIR. - // Closures are carved out by their initial `Assign` statement.) - if !tcx.def_kind(def_id).is_fn_like() { - trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)"); - return false; - } - - // Don't instrument functions with `#[automatically_derived]` on their - // enclosing impl block, on the assumption that most users won't care about - // coverage for derived impls. - if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id()) - && tcx.is_automatically_derived(impl_of) - { - trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)"); - return false; - } - - if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) { - trace!("InstrumentCoverage skipped for {def_id:?} (`#[coverage(off)]`)"); - return false; - } - - true -} - /// Function information extracted from HIR by the coverage instrumentor. #[derive(Debug)] struct ExtractedHirInfo { diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 1354273e2c488..1de7b6f66a7bc 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,16 +1,51 @@ -use super::*; - use rustc_data_structures::captures::Captures; -use rustc_middle::mir::coverage::*; -use rustc_middle::mir::{Body, CoverageIdsInfo}; -use rustc_middle::ty::{self}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::coverage::{CounterId, CoverageKind}; +use rustc_middle::mir::{Body, Coverage, CoverageIdsInfo, Statement, StatementKind}; +use rustc_middle::query::TyCtxtAt; +use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; +use rustc_span::def_id::LocalDefId; /// Registers query/hook implementations related to coverage. pub(crate) fn provide(providers: &mut Providers) { + providers.hooks.is_eligible_for_coverage = + |TyCtxtAt { tcx, .. }, def_id| is_eligible_for_coverage(tcx, def_id); providers.queries.coverage_ids_info = coverage_ids_info; } +/// Hook implementation for [`TyCtxt::is_eligible_for_coverage`]. +fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + // Only instrument functions, methods, and closures (not constants since they are evaluated + // at compile time by Miri). + // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const + // expressions get coverage spans, we will probably have to "carve out" space for const + // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might + // be tricky if const expressions have no corresponding statements in the enclosing MIR. + // Closures are carved out by their initial `Assign` statement.) + if !tcx.def_kind(def_id).is_fn_like() { + trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)"); + return false; + } + + // Don't instrument functions with `#[automatically_derived]` on their + // enclosing impl block, on the assumption that most users won't care about + // coverage for derived impls. + if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id()) + && tcx.is_automatically_derived(impl_of) + { + trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)"); + return false; + } + + if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) { + trace!("InstrumentCoverage skipped for {def_id:?} (`#[coverage(off)]`)"); + return false; + } + + true +} + /// Query implementation for `coverage_ids_info`. fn coverage_ids_info<'tcx>( tcx: TyCtxt<'tcx>, From c921ab17134a7e526ec9df3942890f5b501addcb Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 8 Feb 2024 12:39:15 +1100 Subject: [PATCH 41/79] coverage: Add `CoverageKind::BlockMarker` --- .../rustc_codegen_llvm/src/coverageinfo/mod.rs | 4 ++-- compiler/rustc_middle/src/mir/coverage.rs | 16 ++++++++++++++++ compiler/rustc_middle/src/ty/structural_impls.rs | 1 + .../src/coverage/spans/from_mir.rs | 5 +++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 733a77d24c2a4..133084b7c12ae 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -88,7 +88,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { match coverage.kind { // Marker statements have no effect during codegen, // so return early and don't create `func_coverage`. - CoverageKind::SpanMarker => return, + CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => return, // Match exhaustively to ensure that newly-added kinds are classified correctly. CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {} } @@ -108,7 +108,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { let Coverage { kind } = coverage; match *kind { - CoverageKind::SpanMarker => unreachable!( + CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( "unexpected marker statement {kind:?} should have caused an early return" ), CoverageKind::CounterIncrement { id } => { diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 18e198eb9fcab..ed3a73af1ce27 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -6,6 +6,15 @@ use rustc_span::Symbol; use std::fmt::{self, Debug, Formatter}; +rustc_index::newtype_index! { + /// Used by [`CoverageKind::BlockMarker`] to mark blocks during THIR-to-MIR + /// lowering, so that those blocks can be identified later. + #[derive(HashStable)] + #[encodable] + #[debug_format = "BlockMarkerId({})"] + pub struct BlockMarkerId {} +} + rustc_index::newtype_index! { /// ID of a coverage counter. Values ascend from 0. /// @@ -83,6 +92,12 @@ pub enum CoverageKind { /// codegen. SpanMarker, + /// Marks its enclosing basic block with an ID that can be referred to by + /// other data in the MIR body. + /// + /// Has no effect during codegen. + BlockMarker { id: BlockMarkerId }, + /// Marks the point in MIR control flow represented by a coverage counter. /// /// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR. @@ -107,6 +122,7 @@ impl Debug for CoverageKind { use CoverageKind::*; match self { SpanMarker => write!(fmt, "SpanMarker"), + BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()), CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()), ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()), } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index c8fb11673cf89..4b015640f916e 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -405,6 +405,7 @@ TrivialTypeTraversalImpls! { ::rustc_hir::HirId, ::rustc_hir::MatchSource, ::rustc_target::asm::InlineAsmRegOrRegClass, + crate::mir::coverage::BlockMarkerId, crate::mir::coverage::CounterId, crate::mir::coverage::ExpressionId, crate::mir::Local, diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 099a354f45dd1..223613cfc6fdf 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -225,6 +225,11 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option { Some(statement.source_info.span) } + StatementKind::Coverage(box mir::Coverage { + // Block markers are used for branch coverage, so ignore them here. + kind: CoverageKind::BlockMarker {..} + }) => None, + StatementKind::Coverage(box mir::Coverage { // These coverage statements should not exist prior to coverage instrumentation. kind: CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } From b6312eb943e8d79a54312f4951e049c5bc23d0ba Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 11 Feb 2024 19:08:30 +0300 Subject: [PATCH 42/79] Create some minimal HIR for associated opaque types --- compiler/rustc_ast_lowering/src/index.rs | 1 + compiler/rustc_hir/src/hir.rs | 14 ++++++++- .../rustc_hir_analysis/src/check/wfcheck.rs | 1 + .../src/collect/resolve_bound_vars.rs | 1 + compiler/rustc_hir_pretty/src/lib.rs | 1 + .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 4 ++- .../src/infer/error_reporting/mod.rs | 1 + compiler/rustc_lint/src/late.rs | 2 +- compiler/rustc_lint/src/levels.rs | 1 + compiler/rustc_middle/src/arena.rs | 1 + compiler/rustc_middle/src/hir/map/mod.rs | 10 +++---- compiler/rustc_middle/src/hir/mod.rs | 10 +++---- compiler/rustc_middle/src/query/mod.rs | 8 ++--- compiler/rustc_middle/src/ty/context.rs | 9 ++++-- compiler/rustc_passes/src/reachable.rs | 3 +- compiler/rustc_ty_utils/src/assoc.rs | 29 ++++++++++++++----- 16 files changed, 67 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 11aa6b250b129..793fe9cfd5e2e 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -55,6 +55,7 @@ pub(super) fn index_hir<'hir>( OwnerNode::TraitItem(item) => collector.visit_trait_item(item), OwnerNode::ImplItem(item) => collector.visit_impl_item(item), OwnerNode::ForeignItem(item) => collector.visit_foreign_item(item), + OwnerNode::AssocOpaqueTy(..) => unreachable!(), }; for (local_id, node) in collector.nodes.iter_enumerated() { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 8a7d32997b022..a20ce56fd027b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2553,6 +2553,11 @@ pub struct OpaqueTy<'hir> { pub in_trait: bool, } +#[derive(Copy, Clone, Debug, HashStable_Generic)] +pub struct AssocOpaqueTy { + // Add some data if necessary +} + /// From whence the opaque type came. #[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)] pub enum OpaqueTyOrigin { @@ -3363,6 +3368,7 @@ pub enum OwnerNode<'hir> { TraitItem(&'hir TraitItem<'hir>), ImplItem(&'hir ImplItem<'hir>), Crate(&'hir Mod<'hir>), + AssocOpaqueTy(&'hir AssocOpaqueTy), } impl<'hir> OwnerNode<'hir> { @@ -3372,7 +3378,7 @@ impl<'hir> OwnerNode<'hir> { | OwnerNode::ForeignItem(ForeignItem { ident, .. }) | OwnerNode::ImplItem(ImplItem { ident, .. }) | OwnerNode::TraitItem(TraitItem { ident, .. }) => Some(*ident), - OwnerNode::Crate(..) => None, + OwnerNode::Crate(..) | OwnerNode::AssocOpaqueTy(..) => None, } } @@ -3385,6 +3391,7 @@ impl<'hir> OwnerNode<'hir> { | OwnerNode::ImplItem(ImplItem { span, .. }) | OwnerNode::TraitItem(TraitItem { span, .. }) => span, OwnerNode::Crate(Mod { spans: ModSpans { inner_span, .. }, .. }) => inner_span, + OwnerNode::AssocOpaqueTy(..) => unreachable!(), } } @@ -3443,6 +3450,7 @@ impl<'hir> OwnerNode<'hir> { | OwnerNode::ImplItem(ImplItem { owner_id, .. }) | OwnerNode::ForeignItem(ForeignItem { owner_id, .. }) => *owner_id, OwnerNode::Crate(..) => crate::CRATE_HIR_ID.owner, + OwnerNode::AssocOpaqueTy(..) => unreachable!(), } } @@ -3486,6 +3494,7 @@ impl<'hir> Into> for OwnerNode<'hir> { OwnerNode::ImplItem(n) => Node::ImplItem(n), OwnerNode::TraitItem(n) => Node::TraitItem(n), OwnerNode::Crate(n) => Node::Crate(n), + OwnerNode::AssocOpaqueTy(n) => Node::AssocOpaqueTy(n), } } } @@ -3523,6 +3532,7 @@ pub enum Node<'hir> { WhereBoundPredicate(&'hir WhereBoundPredicate<'hir>), // FIXME: Merge into `Node::Infer`. ArrayLenInfer(&'hir InferArg), + AssocOpaqueTy(&'hir AssocOpaqueTy), // Span by reference to minimize `Node`'s size #[allow(rustc::pass_by_value)] Err(&'hir Span), @@ -3573,6 +3583,7 @@ impl<'hir> Node<'hir> { | Node::Infer(..) | Node::WhereBoundPredicate(..) | Node::ArrayLenInfer(..) + | Node::AssocOpaqueTy(..) | Node::Err(..) => None, } } @@ -3678,6 +3689,7 @@ impl<'hir> Node<'hir> { Node::TraitItem(i) => Some(OwnerNode::TraitItem(i)), Node::ImplItem(i) => Some(OwnerNode::ImplItem(i)), Node::Crate(i) => Some(OwnerNode::Crate(i)), + Node::AssocOpaqueTy(i) => Some(OwnerNode::AssocOpaqueTy(i)), _ => None, } } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 09689b22e905f..ae15efc0764b7 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -196,6 +196,7 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorG hir::OwnerNode::TraitItem(item) => check_trait_item(tcx, item), hir::OwnerNode::ImplItem(item) => check_impl_item(tcx, item), hir::OwnerNode::ForeignItem(item) => check_foreign_item(tcx, item), + hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), }; if let Some(generics) = node.generics() { diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 7936621c86829..d1da2fa0fdc26 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -262,6 +262,7 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou visitor.visit_impl_item(item) } hir::OwnerNode::Crate(_) => {} + hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), } let mut rl = ResolveBoundVars::default(); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index b5bb063c5ed8c..1ace7cd201f96 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -121,6 +121,7 @@ impl<'a> State<'a> { self.print_bounds(":", pred.bounds); } Node::ArrayLenInfer(_) => self.word("_"), + Node::AssocOpaqueTy(..) => unreachable!(), Node::Err(_) => self.word("/*ERROR*/"), } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a08582a67d94c..2747700f3c136 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2174,7 +2174,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut call_finder = FindClosureArg { tcx: self.tcx, calls: vec![] }; let node = self .tcx - .opt_local_def_id_to_hir_id(self.tcx.hir().get_parent_item(call_expr.hir_id)) + .opt_local_def_id_to_hir_id( + self.tcx.hir().get_parent_item(call_expr.hir_id).def_id, + ) .map(|hir_id| self.tcx.hir_node(hir_id)); match node { Some(hir::Node::Item(item)) => call_finder.visit_item(item), diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 222c0a3954255..edd7f733ec96b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2554,6 +2554,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i), hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i), hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"), + hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), } let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap(); diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index d3d7698e7f927..506716e39a1e8 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -356,7 +356,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( cached_typeck_results: Cell::new(None), param_env: ty::ParamEnv::empty(), effective_visibilities: tcx.effective_visibilities(()), - last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(module_def_id.into()), + last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(module_def_id), generics: None, only_module: true, }; diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index e89df1c9840c6..ee24cd0e4eeb3 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -190,6 +190,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe levels.add_id(hir::CRATE_HIR_ID); levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID) } + hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), }, } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index a532635669dfd..c90427256b85f 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -115,6 +115,7 @@ macro_rules! arena_types { [] features: rustc_feature::Features, [decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph, [] crate_inherent_impls: rustc_middle::ty::CrateInherentImpls, + [] hir_owner_nodes: rustc_hir::OwnerNodes<'tcx>, ]); ) } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index c05da36235851..a64fa74762c04 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -161,7 +161,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Retrieves the `hir::Node` corresponding to `id`, returning `None` if cannot be found. #[inline] pub fn opt_hir_node_by_def_id(self, id: LocalDefId) -> Option> { - Some(self.hir_node(self.opt_local_def_id_to_hir_id(id)?)) + Some(self.hir_node_by_def_id(id)) } /// Retrieves the `hir::Node` corresponding to `id`. @@ -169,12 +169,10 @@ impl<'tcx> TyCtxt<'tcx> { self.hir_owner_nodes(id.owner).nodes[id.local_id].node } - /// Retrieves the `hir::Node` corresponding to `id`, panicking if it cannot be found. + /// Retrieves the `hir::Node` corresponding to `id`. #[inline] - #[track_caller] pub fn hir_node_by_def_id(self, id: LocalDefId) -> Node<'tcx> { - self.opt_hir_node_by_def_id(id) - .unwrap_or_else(|| bug!("couldn't find HIR node for def id {id:?}")) + self.hir_node(self.local_def_id_to_hir_id(id)) } /// Returns `HirId` of the parent HIR node of node with this `hir_id`. @@ -963,6 +961,7 @@ impl<'hir> Map<'hir> { Node::Crate(item) => item.spans.inner_span, Node::WhereBoundPredicate(pred) => pred.span, Node::ArrayLenInfer(inf) => inf.span, + Node::AssocOpaqueTy(..) => unreachable!(), Node::Err(span) => *span, } } @@ -1227,6 +1226,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::Crate(..) => String::from("(root_crate)"), Node::WhereBoundPredicate(_) => node_str("where bound predicate"), Node::ArrayLenInfer(_) => node_str("array len infer"), + Node::AssocOpaqueTy(..) => unreachable!(), Node::Err(_) => node_str("error"), } } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 4ef9bc16221f2..61bdb5d4bb705 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -127,12 +127,10 @@ pub fn provide(providers: &mut Providers) { providers.hir_crate_items = map::hir_crate_items; providers.crate_hash = map::crate_hash; providers.hir_module_items = map::hir_module_items; - providers.opt_local_def_id_to_hir_id = |tcx, def_id| { - Some(match tcx.hir_crate(()).owners[def_id] { - MaybeOwner::Owner(_) => HirId::make_owner(def_id), - MaybeOwner::NonOwner(hir_id) => hir_id, - MaybeOwner::Phantom => bug!("No HirId for {:?}", def_id), - }) + providers.local_def_id_to_hir_id = |tcx, def_id| match tcx.hir_crate(()).owners[def_id] { + MaybeOwner::Owner(_) => HirId::make_owner(def_id), + MaybeOwner::NonOwner(hir_id) => hir_id, + MaybeOwner::Phantom => bug!("No HirId for {:?}", def_id), }; providers.opt_hir_owner_nodes = |tcx, id| tcx.hir_crate(()).owners.get(id)?.as_owner().map(|i| &i.nodes); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 83ded5859c67e..865299e15c803 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -174,10 +174,8 @@ rustc_queries! { cache_on_disk_if { true } } - /// Gives access to the HIR ID for the given `LocalDefId` owner `key` if any. - /// - /// Definitions that were generated with no HIR, would be fed to return `None`. - query opt_local_def_id_to_hir_id(key: LocalDefId) -> Option{ + /// Returns HIR ID for the given `LocalDefId`. + query local_def_id_to_hir_id(key: LocalDefId) -> hir::HirId { desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key) } feedable } @@ -196,6 +194,7 @@ rustc_queries! { /// Avoid calling this query directly. query opt_hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> { desc { |tcx| "getting HIR owner items in `{}`", tcx.def_path_str(key) } + feedable } /// Gives access to the HIR attributes inside the HIR owner `key`. @@ -204,6 +203,7 @@ rustc_queries! { /// Avoid calling this query directly. query hir_attrs(key: hir::OwnerId) -> &'tcx hir::AttributeMap<'tcx> { desc { |tcx| "getting HIR owner attributes in `{}`", tcx.def_path_str(key) } + feedable } /// Given the def_id of a const-generic parameter, computes the associated default const diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c415c06c21b96..5362b6d8b24ca 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -589,6 +589,11 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { pub fn def_id(&self) -> LocalDefId { self.key } + + // Caller must ensure that `self.key` ID is indeed an owner. + pub fn feed_owner_id(&self) -> TyCtxtFeed<'tcx, hir::OwnerId> { + TyCtxtFeed { tcx: self.tcx, key: hir::OwnerId { def_id: self.key } } + } } /// The central data structure of the compiler. It stores references @@ -2350,8 +2355,8 @@ impl<'tcx> TyCtxt<'tcx> { self.intrinsic_raw(def_id) } - pub fn local_def_id_to_hir_id(self, local_def_id: LocalDefId) -> HirId { - self.opt_local_def_id_to_hir_id(local_def_id).unwrap() + pub fn opt_local_def_id_to_hir_id(self, local_def_id: LocalDefId) -> Option { + Some(self.local_def_id_to_hir_id(local_def_id)) } pub fn next_trait_solver_globally(self) -> bool { diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index e86c0522b3cd4..2a78f47c34f67 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -270,7 +270,8 @@ impl<'tcx> ReachableContext<'tcx> { | Node::Ctor(..) | Node::Field(_) | Node::Ty(_) - | Node::Crate(_) => {} + | Node::Crate(_) + | Node::AssocOpaqueTy(..) => {} _ => { bug!( "found unexpected node kind in worklist: {} ({:?})", diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 26d3370469a5d..3f628092190b5 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -1,10 +1,11 @@ use rustc_data_structures::fx::FxIndexSet; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir, HirId}; +use rustc_index::IndexVec; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; +use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt, TyCtxtFeed}; use rustc_span::symbol::kw; pub(crate) fn provide(providers: &mut Providers) { @@ -237,6 +238,22 @@ fn associated_types_for_impl_traits_in_associated_fn( } } +fn feed_hir(feed: &TyCtxtFeed<'_, LocalDefId>) { + feed.local_def_id_to_hir_id(HirId::make_owner(feed.def_id())); + feed.opt_hir_owner_nodes(Some(feed.tcx.arena.alloc(hir::OwnerNodes { + opt_hash_including_bodies: None, + nodes: IndexVec::from_elem_n( + hir::ParentedNode { + parent: hir::ItemLocalId::INVALID, + node: hir::Node::AssocOpaqueTy(&hir::AssocOpaqueTy {}), + }, + 1, + ), + bodies: Default::default(), + }))); + feed.feed_owner_id().hir_attrs(hir::AttributeMap::EMPTY); +} + /// Given an `opaque_ty_def_id` corresponding to an `impl Trait` in an associated /// function from a trait, synthesize an associated type for that `impl Trait` /// that inherits properties that we infer from the method and the opaque type. @@ -258,9 +275,7 @@ fn associated_type_for_impl_trait_in_trait( let local_def_id = trait_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); - // There's no HIR associated with this new synthesized `def_id`, so feed - // `opt_local_def_id_to_hir_id` with `None`. - trait_assoc_ty.opt_local_def_id_to_hir_id(None); + feed_hir(&trait_assoc_ty); // Copy span of the opaque. trait_assoc_ty.def_ident_span(Some(span)); @@ -318,9 +333,7 @@ fn associated_type_for_impl_trait_in_impl( let local_def_id = impl_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); - // There's no HIR associated with this new synthesized `def_id`, so feed - // `opt_local_def_id_to_hir_id` with `None`. - impl_assoc_ty.opt_local_def_id_to_hir_id(None); + feed_hir(&impl_assoc_ty); // Copy span of the opaque. impl_assoc_ty.def_ident_span(Some(span)); From 12cd3220624fe50d039b268cc5ff35c7895ef293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Wed, 28 Feb 2024 22:42:31 +0100 Subject: [PATCH 43/79] Make incremental sessions identity no longer depend on the crate names provided by source code --- compiler/rustc_driver_impl/src/lib.rs | 3 +- compiler/rustc_incremental/src/persist/fs.rs | 22 +++--- .../rustc_incremental/src/persist/load.rs | 12 ++-- compiler/rustc_interface/messages.ftl | 3 - compiler/rustc_interface/src/errors.rs | 9 --- compiler/rustc_interface/src/queries.rs | 6 +- compiler/rustc_interface/src/util.rs | 68 ++----------------- compiler/rustc_session/messages.ftl | 3 + compiler/rustc_session/src/errors.rs | 9 ++- compiler/rustc_session/src/output.rs | 65 +++++++++++++++++- 10 files changed, 101 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index c448835413571..938f9f0beaacd 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -28,7 +28,7 @@ use rustc_errors::{ markdown, ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult, }; use rustc_feature::find_gated_cfg; -use rustc_interface::util::{self, collect_crate_types, get_codegen_backend}; +use rustc_interface::util::{self, get_codegen_backend}; use rustc_interface::{interface, Queries}; use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; @@ -37,6 +37,7 @@ use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS}; use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType}; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; +use rustc_session::output::collect_crate_types; use rustc_session::{config, filesearch, EarlyDiagCtxt, Session}; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::FileLoader; diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index dd9c16d006a96..1462037c8c8e7 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -110,8 +110,9 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; use rustc_errors::ErrorGuaranteed; use rustc_fs_util::{link_or_copy, try_canonicalize, LinkOrCopy}; +use rustc_session::config::CrateType; +use rustc_session::output::{collect_crate_types, find_crate_name}; use rustc_session::{Session, StableCrateId}; -use rustc_span::Symbol; use std::fs as std_fs; use std::io::{self, ErrorKind}; @@ -205,11 +206,7 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu /// The garbage collection will take care of it. /// /// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph -pub(crate) fn prepare_session_directory( - sess: &Session, - crate_name: Symbol, - stable_crate_id: StableCrateId, -) -> Result<(), ErrorGuaranteed> { +pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuaranteed> { if sess.opts.incremental.is_none() { return Ok(()); } @@ -219,7 +216,7 @@ pub(crate) fn prepare_session_directory( debug!("prepare_session_directory"); // {incr-comp-dir}/{crate-name-and-disambiguator} - let crate_dir = crate_path(sess, crate_name, stable_crate_id); + let crate_dir = crate_path(sess); debug!("crate-dir: {}", crate_dir.display()); create_dir(sess, &crate_dir, "crate")?; @@ -604,9 +601,18 @@ fn string_to_timestamp(s: &str) -> Result { Ok(UNIX_EPOCH + duration) } -fn crate_path(sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId) -> PathBuf { +fn crate_path(sess: &Session) -> PathBuf { let incr_dir = sess.opts.incremental.as_ref().unwrap().clone(); + let crate_name = find_crate_name(sess, &[]); + let crate_types = collect_crate_types(sess, &[]); + let stable_crate_id = StableCrateId::new( + crate_name, + crate_types.contains(&CrateType::Executable), + sess.opts.cg.metadata.clone(), + sess.cfg_version, + ); + let stable_crate_id = base_n::encode(stable_crate_id.as_u64() as u128, INT_ENCODE_BASE); let crate_name = format!("{crate_name}-{stable_crate_id}"); diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 96bfe766c20f5..357f2ae92d4c3 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -8,8 +8,8 @@ use rustc_middle::query::on_disk_cache::OnDiskCache; use rustc_serialize::opaque::MemDecoder; use rustc_serialize::Decodable; use rustc_session::config::IncrementalStateAssertion; -use rustc_session::{Session, StableCrateId}; -use rustc_span::{ErrorGuaranteed, Symbol}; +use rustc_session::Session; +use rustc_span::ErrorGuaranteed; use std::path::{Path, PathBuf}; use super::data::*; @@ -190,13 +190,9 @@ pub fn load_query_result_cache(sess: &Session) -> Option> { /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a /// new graph to an incremental session directory. -pub fn setup_dep_graph( - sess: &Session, - crate_name: Symbol, - stable_crate_id: StableCrateId, -) -> Result { +pub fn setup_dep_graph(sess: &Session) -> Result { // `load_dep_graph` can only be called after `prepare_session_directory`. - prepare_session_directory(sess, crate_name, stable_crate_id)?; + prepare_session_directory(sess)?; let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess)); diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index bd9fad8b04296..47dfbc1d7fbf1 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -48,6 +48,3 @@ interface_rustc_error_unexpected_annotation = interface_temps_dir_error = failed to find or create the directory specified by `--temps-dir` - -interface_unsupported_crate_type_for_target = - dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index a9ab2720d89a5..29294003b8f29 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -1,7 +1,5 @@ use rustc_macros::Diagnostic; -use rustc_session::config::CrateType; use rustc_span::{Span, Symbol}; -use rustc_target::spec::TargetTriple; use std::io; use std::path::Path; @@ -90,13 +88,6 @@ pub struct FailedWritingFile<'a> { #[diag(interface_proc_macro_crate_panic_abort)] pub struct ProcMacroCratePanicAbort; -#[derive(Diagnostic)] -#[diag(interface_unsupported_crate_type_for_target)] -pub struct UnsupportedCrateTypeForTarget<'a> { - pub crate_type: CrateType, - pub target_triple: &'a TargetTriple, -} - #[derive(Diagnostic)] #[diag(interface_multiple_output_types_adaption)] pub struct MultipleOutputTypesAdaption; diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index c22188226961e..ee677a092e2f1 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::{GlobalCtxt, TyCtxt}; use rustc_serialize::opaque::FileEncodeResult; use rustc_session::config::{self, CrateType, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; -use rustc_session::output::find_crate_name; +use rustc_session::output::{collect_crate_types, find_crate_name}; use rustc_session::Session; use rustc_span::symbol::sym; use std::any::Any; @@ -128,7 +128,7 @@ impl<'tcx> Queries<'tcx> { // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches. let crate_name = find_crate_name(sess, &pre_configured_attrs); - let crate_types = util::collect_crate_types(sess, &pre_configured_attrs); + let crate_types = collect_crate_types(sess, &pre_configured_attrs); let stable_crate_id = StableCrateId::new( crate_name, crate_types.contains(&CrateType::Executable), @@ -136,7 +136,7 @@ impl<'tcx> Queries<'tcx> { sess.cfg_version, ); let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id)?; + let dep_graph = setup_dep_graph(sess)?; let cstore = FreezeLock::new(Box::new(CStore::new( self.compiler.codegen_backend.metadata_loader(), diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 829b00aabc126..7d48f90db3625 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -7,14 +7,15 @@ use rustc_data_structures::sync; use rustc_metadata::{load_symbol_from_dylib, DylibError}; use rustc_parse::validate_attr; use rustc_session as session; -use rustc_session::config::{self, Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes}; +use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes}; use rustc_session::filesearch::sysroot_candidates; use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; -use rustc_session::{filesearch, output, Session}; +use rustc_session::{filesearch, Session}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::sym; use rustc_target::spec::Target; +use session::output::{categorize_crate_type, CRATE_TYPES}; use session::EarlyDiagCtxt; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::path::{Path, PathBuf}; @@ -399,67 +400,6 @@ pub(crate) fn check_attr_crate_type( } } -const CRATE_TYPES: &[(Symbol, CrateType)] = &[ - (sym::rlib, CrateType::Rlib), - (sym::dylib, CrateType::Dylib), - (sym::cdylib, CrateType::Cdylib), - (sym::lib, config::default_lib_output()), - (sym::staticlib, CrateType::Staticlib), - (sym::proc_dash_macro, CrateType::ProcMacro), - (sym::bin, CrateType::Executable), -]; - -fn categorize_crate_type(s: Symbol) -> Option { - Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1) -} - -pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { - // If we're generating a test executable, then ignore all other output - // styles at all other locations - if session.opts.test { - return vec![CrateType::Executable]; - } - - // Only check command line flags if present. If no types are specified by - // command line, then reuse the empty `base` Vec to hold the types that - // will be found in crate attributes. - // JUSTIFICATION: before wrapper fn is available - #[allow(rustc::bad_opt_access)] - let mut base = session.opts.crate_types.clone(); - if base.is_empty() { - let attr_types = attrs.iter().filter_map(|a| { - if a.has_name(sym::crate_type) - && let Some(s) = a.value_str() - { - categorize_crate_type(s) - } else { - None - } - }); - base.extend(attr_types); - if base.is_empty() { - base.push(output::default_output_for_target(session)); - } else { - base.sort(); - base.dedup(); - } - } - - base.retain(|crate_type| { - if output::invalid_output_for_target(session, *crate_type) { - session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget { - crate_type: *crate_type, - target_triple: &session.opts.target_triple, - }); - false - } else { - true - } - }); - - base -} - fn multiple_output_types_to_stdout( output_types: &OutputTypes, single_output_file_is_stdout: bool, diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index af8c962a2ed6a..42c681e496174 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -111,4 +111,7 @@ session_unleashed_feature_help_unnamed = skipping check that does not even have session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto` +session_unsupported_crate_type_for_target = + dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` + session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5 diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 5f04915a9e755..d523da1ad7e38 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -10,7 +10,7 @@ use rustc_macros::Diagnostic; use rustc_span::{Span, Symbol}; use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple}; -use crate::parse::ParseSess; +use crate::{config::CrateType, parse::ParseSess}; pub struct FeatureGateError { pub span: MultiSpan, @@ -345,6 +345,13 @@ pub(crate) struct BinaryFloatLiteralNotSupported { pub span: Span, } +#[derive(Diagnostic)] +#[diag(session_unsupported_crate_type_for_target)] +pub struct UnsupportedCrateTypeForTarget<'a> { + pub crate_type: CrateType, + pub target_triple: &'a TargetTriple, +} + pub fn report_lit_error( psess: &ParseSess, err: LitError, diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 74d26237f2463..35cd3cbab66ce 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -1,7 +1,7 @@ //! Related to out filenames of compilation (e.g. binaries). -use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; +use crate::config::{self, CrateType, Input, OutFileName, OutputFilenames, OutputType}; use crate::errors::{ - CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, + self, CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, InvalidCharacterInCrateName, InvalidCrateNameHelp, }; use crate::Session; @@ -200,3 +200,64 @@ pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool false } + +pub const CRATE_TYPES: &[(Symbol, CrateType)] = &[ + (sym::rlib, CrateType::Rlib), + (sym::dylib, CrateType::Dylib), + (sym::cdylib, CrateType::Cdylib), + (sym::lib, config::default_lib_output()), + (sym::staticlib, CrateType::Staticlib), + (sym::proc_dash_macro, CrateType::ProcMacro), + (sym::bin, CrateType::Executable), +]; + +pub fn categorize_crate_type(s: Symbol) -> Option { + Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1) +} + +pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { + // If we're generating a test executable, then ignore all other output + // styles at all other locations + if session.opts.test { + return vec![CrateType::Executable]; + } + + // Only check command line flags if present. If no types are specified by + // command line, then reuse the empty `base` Vec to hold the types that + // will be found in crate attributes. + // JUSTIFICATION: before wrapper fn is available + #[allow(rustc::bad_opt_access)] + let mut base = session.opts.crate_types.clone(); + if base.is_empty() { + let attr_types = attrs.iter().filter_map(|a| { + if a.has_name(sym::crate_type) + && let Some(s) = a.value_str() + { + categorize_crate_type(s) + } else { + None + } + }); + base.extend(attr_types); + if base.is_empty() { + base.push(default_output_for_target(session)); + } else { + base.sort(); + base.dedup(); + } + } + + base.retain(|crate_type| { + if invalid_output_for_target(session, *crate_type) { + session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget { + crate_type: *crate_type, + target_triple: &session.opts.target_triple, + }); + false + } else { + true + } + }); + + base +} From 514b2745b35ecc8c8c0b8ec8c12bde3fa48cb464 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 13 Mar 2024 15:07:47 +0100 Subject: [PATCH 44/79] const-eval: organize and extend tests for required-consts --- .../consts/const-eval/erroneous-const.stderr | 15 ------- .../ui/consts/const-eval/erroneous-const2.rs | 19 --------- .../consts/const-eval/erroneous-const2.stderr | 15 ------- .../unused-broken-const-late.stderr | 11 ----- .../collect-in-called-fn.noopt.stderr | 17 ++++++++ .../collect-in-called-fn.opt.stderr | 17 ++++++++ .../collect-in-called-fn.rs} | 18 ++++---- .../collect-in-dead-drop.noopt.stderr | 14 +++++++ .../required-consts/collect-in-dead-drop.rs | 33 +++++++++++++++ .../collect-in-dead-fn.noopt.stderr | 17 ++++++++ .../required-consts/collect-in-dead-fn.rs | 35 ++++++++++++++++ .../required-consts/collect-in-dead-forget.rs | 32 +++++++++++++++ .../collect-in-dead-move.noopt.stderr | 14 +++++++ .../required-consts/collect-in-dead-move.rs | 33 +++++++++++++++ .../collect-in-dead-vtable.noopt.stderr | 17 ++++++++ .../required-consts/collect-in-dead-vtable.rs | 41 +++++++++++++++++++ .../interpret-in-const-called-fn.noopt.stderr | 17 ++++++++ .../interpret-in-const-called-fn.opt.stderr | 17 ++++++++ .../interpret-in-const-called-fn.rs} | 13 +++--- .../interpret-in-promoted.noopt.stderr | 27 ++++++++++++ .../interpret-in-promoted.opt.stderr | 27 ++++++++++++ .../required-consts/interpret-in-promoted.rs | 15 +++++++ .../interpret-in-static.noopt.stderr | 17 ++++++++ .../interpret-in-static.opt.stderr | 17 ++++++++ .../required-consts/interpret-in-static.rs | 21 ++++++++++ 25 files changed, 447 insertions(+), 72 deletions(-) delete mode 100644 tests/ui/consts/const-eval/erroneous-const.stderr delete mode 100644 tests/ui/consts/const-eval/erroneous-const2.rs delete mode 100644 tests/ui/consts/const-eval/erroneous-const2.stderr delete mode 100644 tests/ui/consts/const-eval/unused-broken-const-late.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr rename tests/ui/consts/{const-eval/unused-broken-const-late.rs => required-consts/collect-in-called-fn.rs} (55%) create mode 100644 tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-drop.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-fn.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-forget.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-move.rs create mode 100644 tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr create mode 100644 tests/ui/consts/required-consts/collect-in-dead-vtable.rs create mode 100644 tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr create mode 100644 tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr rename tests/ui/consts/{const-eval/erroneous-const.rs => required-consts/interpret-in-const-called-fn.rs} (52%) create mode 100644 tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr create mode 100644 tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr create mode 100644 tests/ui/consts/required-consts/interpret-in-promoted.rs create mode 100644 tests/ui/consts/required-consts/interpret-in-static.noopt.stderr create mode 100644 tests/ui/consts/required-consts/interpret-in-static.opt.stderr create mode 100644 tests/ui/consts/required-consts/interpret-in-static.rs diff --git a/tests/ui/consts/const-eval/erroneous-const.stderr b/tests/ui/consts/const-eval/erroneous-const.stderr deleted file mode 100644 index bd25e96c2cf13..0000000000000 --- a/tests/ui/consts/const-eval/erroneous-const.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0080]: evaluation of `PrintName::::VOID` failed - --> $DIR/erroneous-const.rs:6:22 - | -LL | const VOID: () = [()][2]; - | ^^^^^^^ index out of bounds: the length is 1 but the index is 2 - -note: erroneous constant encountered - --> $DIR/erroneous-const.rs:13:13 - | -LL | PrintName::::VOID; - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/erroneous-const2.rs b/tests/ui/consts/const-eval/erroneous-const2.rs deleted file mode 100644 index 61f2955f2d822..0000000000000 --- a/tests/ui/consts/const-eval/erroneous-const2.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Make sure we error on erroneous consts even if they are unused. -#![allow(unconditional_panic)] - -struct PrintName(T); -impl PrintName { - const VOID: () = [()][2]; //~ERROR evaluation of `PrintName::::VOID` failed -} - -pub static FOO: () = { - if false { - // This bad constant is only used in dead code in a static initializer... and yet we still - // must make sure that the build fails. - PrintName::::VOID; //~ constant - } -}; - -fn main() { - FOO -} diff --git a/tests/ui/consts/const-eval/erroneous-const2.stderr b/tests/ui/consts/const-eval/erroneous-const2.stderr deleted file mode 100644 index 6a5839e3dfb41..0000000000000 --- a/tests/ui/consts/const-eval/erroneous-const2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0080]: evaluation of `PrintName::::VOID` failed - --> $DIR/erroneous-const2.rs:6:22 - | -LL | const VOID: () = [()][2]; - | ^^^^^^^ index out of bounds: the length is 1 but the index is 2 - -note: erroneous constant encountered - --> $DIR/erroneous-const2.rs:13:9 - | -LL | PrintName::::VOID; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/unused-broken-const-late.stderr b/tests/ui/consts/const-eval/unused-broken-const-late.stderr deleted file mode 100644 index c2cf2f3813c5e..0000000000000 --- a/tests/ui/consts/const-eval/unused-broken-const-late.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: evaluation of `PrintName::::VOID` failed - --> $DIR/unused-broken-const-late.rs:8:22 - | -LL | const VOID: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/unused-broken-const-late.rs:8:22 - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr new file mode 100644 index 0000000000000..c7ff1328917fc --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-called-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn called::` + --> $DIR/collect-in-called-fn.rs:23:5 + | +LL | called::(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr new file mode 100644 index 0000000000000..c7ff1328917fc --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-called-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn called::` + --> $DIR/collect-in-called-fn.rs:23:5 + | +LL | called::(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/unused-broken-const-late.rs b/tests/ui/consts/required-consts/collect-in-called-fn.rs similarity index 55% rename from tests/ui/consts/const-eval/unused-broken-const-late.rs rename to tests/ui/consts/required-consts/collect-in-called-fn.rs index c4916061f9e5a..55133a10cd988 100644 --- a/tests/ui/consts/const-eval/unused-broken-const-late.rs +++ b/tests/ui/consts/required-consts/collect-in-called-fn.rs @@ -1,20 +1,24 @@ +//@revisions: noopt opt //@ build-fail -//@ compile-flags: -O +//@[opt] compile-flags: -O //! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is //! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) -struct PrintName(T); -impl PrintName { - const VOID: () = panic!(); //~ERROR evaluation of `PrintName::::VOID` failed +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } -fn no_codegen() { +#[inline(never)] +fn called() { // Any function that is called is guaranteed to have all consts that syntactically // appear in its body evaluated, even if they only appear in dead code. + // This relies on mono-item collection checking `required_consts` in collected functions. if false { - let _ = PrintName::::VOID; + let _ = Fail::::C; } } + pub fn main() { - no_codegen::(); + called::(); } diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr new file mode 100644 index 0000000000000..b7010e787633b --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr @@ -0,0 +1,14 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-drop.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn as std::ops::Drop>::drop` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.rs b/tests/ui/consts/required-consts/collect-in-dead-drop.rs new file mode 100644 index 0000000000000..c9ffcec690317 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.rs @@ -0,0 +1,33 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +// This function is not actually called, but is mentioned implicitly as destructor in dead code in a +// function that is called. Make sure we still find this error. +impl Drop for Fail { + fn drop(&mut self) { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called(x: T) { + if false { + let v = Fail(x); + // Now it gest dropped implicitly, at the end of this scope. + } +} + +pub fn main() { + called::(0); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr new file mode 100644 index 0000000000000..2162c35c83702 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/collect-in-dead-fn.rs:29:9 + | +LL | not_called::(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.rs b/tests/ui/consts/required-consts/collect-in-dead-fn.rs new file mode 100644 index 0000000000000..9e6b151915331 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.rs @@ -0,0 +1,35 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +// This function is not actually called, but it is mentioned in dead code in a function that is +// called. Make sure we still find this error. +// This relies on mono-item collection checking `required_consts` in functions that syntactically +// are called in collected functions (even inside dead code). +#[inline(never)] +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called() { + if false { + not_called::(); + } +} + +pub fn main() { + called::(); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-forget.rs b/tests/ui/consts/required-consts/collect-in-dead-forget.rs new file mode 100644 index 0000000000000..720b7a499f781 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-forget.rs @@ -0,0 +1,32 @@ +//@revisions: noopt opt +//@build-pass +//@[opt] compile-flags: -O +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); +} + +// This function is not actually called, but is mentioned implicitly as destructor in dead code in a +// function that is called. Make sure we still find this error. +impl Drop for Fail { + fn drop(&mut self) { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called(x: T) { + if false { + let v = Fail(x); + std::mem::forget(v); + // Now the destructor never gets "mentioned" so this build should *not* fail. + // IOW, this demonstrates that we are using a post-drop-elab notion of "mentioned". + } +} + +pub fn main() { + called::(0); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr new file mode 100644 index 0000000000000..8c853127e044c --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr @@ -0,0 +1,14 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-move.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn as std::ops::Drop>::drop` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.rs b/tests/ui/consts/required-consts/collect-in-dead-move.rs new file mode 100644 index 0000000000000..f3a6ba8a6577c --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-move.rs @@ -0,0 +1,33 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +// This function is not actually called, but is mentioned implicitly as destructor in dead code in a +// function that is called. Make sure we still find this error. +impl Drop for Fail { + fn drop(&mut self) { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called(x: T) { + if false { + let v = Fail(x); + drop(v); // move `v` away (and it then gets dropped there so build still fails) + } +} + +pub fn main() { + called::(0); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr new file mode 100644 index 0000000000000..6fd82777bd364 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-vtable.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn as MyTrait>::not_called` + --> $DIR/collect-in-dead-vtable.rs:35:40 + | +LL | let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here + | ^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.rs b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs new file mode 100644 index 0000000000000..f21a1cc1fc2fd --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs @@ -0,0 +1,41 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +trait MyTrait { + fn not_called(&self); +} + +// This function is not actually called, but it is mentioned in a vtable in a function that is +// called. Make sure we still find this error. +// This relies on mono-item collection checking `required_consts` in functions that are referenced +// in vtables that syntactically appear in collected functions (even inside dead code). +impl MyTrait for Vec { + fn not_called(&self) { + if false { + let _ = Fail::::C; + } + } +} + +#[inline(never)] +fn called() { + if false { + let v: Vec = Vec::new(); + let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here + } +} + +pub fn main() { + called::(); +} diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr new file mode 100644 index 0000000000000..75304591b9f05 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/interpret-in-const-called-fn.rs:7:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:7:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/interpret-in-const-called-fn.rs:16:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr new file mode 100644 index 0000000000000..75304591b9f05 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/interpret-in-const-called-fn.rs:7:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:7:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/interpret-in-const-called-fn.rs:16:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/erroneous-const.rs b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs similarity index 52% rename from tests/ui/consts/const-eval/erroneous-const.rs rename to tests/ui/consts/required-consts/interpret-in-const-called-fn.rs index 74d44c5259a3e..c409fae0bb905 100644 --- a/tests/ui/consts/const-eval/erroneous-const.rs +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs @@ -1,16 +1,19 @@ +//@revisions: noopt opt +//@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. -#![allow(unconditional_panic)] -struct PrintName(T); -impl PrintName { - const VOID: () = [()][2]; //~ERROR evaluation of `PrintName::::VOID` failed +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } +#[inline(never)] const fn no_codegen() { if false { // This bad constant is only used in dead code in a no-codegen function... and yet we still // must make sure that the build fails. - PrintName::::VOID; //~ constant + // This relies on const-eval evaluating all `required_consts` of `const fn`. + Fail::::C; //~ constant } } diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr new file mode 100644 index 0000000000000..491131daf8de4 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr @@ -0,0 +1,27 @@ +error[E0080]: evaluation of constant value failed + --> $SRC_DIR/core/src/hint.rs:LL:COL + | + = note: entering unreachable code + | +note: inside `unreachable_unchecked` + --> $SRC_DIR/core/src/hint.rs:LL:COL +note: inside `ub` + --> $DIR/interpret-in-promoted.rs:6:5 + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: inside `FOO` + --> $DIR/interpret-in-promoted.rs:12:28 + | +LL | let _x: &'static () = &ub(); + | ^^^^ + +note: erroneous constant encountered + --> $DIR/interpret-in-promoted.rs:12:27 + | +LL | let _x: &'static () = &ub(); + | ^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr new file mode 100644 index 0000000000000..491131daf8de4 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr @@ -0,0 +1,27 @@ +error[E0080]: evaluation of constant value failed + --> $SRC_DIR/core/src/hint.rs:LL:COL + | + = note: entering unreachable code + | +note: inside `unreachable_unchecked` + --> $SRC_DIR/core/src/hint.rs:LL:COL +note: inside `ub` + --> $DIR/interpret-in-promoted.rs:6:5 + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: inside `FOO` + --> $DIR/interpret-in-promoted.rs:12:28 + | +LL | let _x: &'static () = &ub(); + | ^^^^ + +note: erroneous constant encountered + --> $DIR/interpret-in-promoted.rs:12:27 + | +LL | let _x: &'static () = &ub(); + | ^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.rs b/tests/ui/consts/required-consts/interpret-in-promoted.rs new file mode 100644 index 0000000000000..9c2cf4e70d3fb --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-promoted.rs @@ -0,0 +1,15 @@ +//@revisions: noopt opt +//@[opt] compile-flags: -O +//! Make sure we error on erroneous consts even if they are unused. + +const unsafe fn ub() { + std::hint::unreachable_unchecked(); +} + +pub const FOO: () = unsafe { + // Make sure that this gets promoted and then fails to evaluate, and we deal with that + // correctly. + let _x: &'static () = &ub(); //~ erroneous constant +}; + +fn main() {} diff --git a/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr new file mode 100644 index 0000000000000..159c9449fc041 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/interpret-in-static.rs:7:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:7:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/interpret-in-static.rs:15:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-static.opt.stderr b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr new file mode 100644 index 0000000000000..159c9449fc041 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/interpret-in-static.rs:7:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:7:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/interpret-in-static.rs:15:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-static.rs b/tests/ui/consts/required-consts/interpret-in-static.rs new file mode 100644 index 0000000000000..559e281b2b038 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-static.rs @@ -0,0 +1,21 @@ +//@revisions: noopt opt +//@[opt] compile-flags: -O +//! Make sure we error on erroneous consts even if they are unused. + +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed +} + +pub static FOO: () = { + if false { + // This bad constant is only used in dead code in a static initializer... and yet we still + // must make sure that the build fails. + // This relies on const-eval evaluating all `required_consts` of the `static` MIR body. + Fail::::C; //~ constant + } +}; + +fn main() { + FOO +} From f2ec0d3d26dc31c84274b853cc04ce25371c8d3d Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 14 Mar 2024 01:37:12 +0300 Subject: [PATCH 45/79] Implement `Duration::as_millis_{f64,f32}` --- library/core/src/time.rs | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/library/core/src/time.rs b/library/core/src/time.rs index b533f53993870..78494b866b108 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -856,6 +856,48 @@ impl Duration { (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32) } + /// Returns the number of milliseconds contained by this `Duration` as `f64`. + /// + /// The returned value does include the fractional (nanosecond) part of the duration. + /// + /// # Examples + /// ``` + /// #![feature(duration_millis_float)] + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 345_678_000); + /// assert_eq!(dur.as_millis_f64(), 2345.678); + /// ``` + #[unstable(feature = "duration_millis_float", issue = "122451")] + #[must_use] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] + pub const fn as_millis_f64(&self) -> f64 { + (self.secs as f64) * (MILLIS_PER_SEC as f64) + + (self.nanos.0 as f64) / (NANOS_PER_MILLI as f64) + } + + /// Returns the number of milliseconds contained by this `Duration` as `f32`. + /// + /// The returned value does include the fractional (nanosecond) part of the duration. + /// + /// # Examples + /// ``` + /// #![feature(duration_millis_float)] + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 345_678_000); + /// assert_eq!(dur.as_millis_f32(), 2345.678); + /// ``` + #[unstable(feature = "duration_millis_float", issue = "122451")] + #[must_use] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] + pub const fn as_millis_f32(&self) -> f32 { + (self.secs as f32) * (MILLIS_PER_SEC as f32) + + (self.nanos.0 as f32) / (NANOS_PER_MILLI as f32) + } + /// Creates a new `Duration` from the specified number of seconds represented /// as `f64`. /// From be33586adc13444e7d08c4269344db2dce6c2a03 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Wed, 13 Mar 2024 22:38:05 +0100 Subject: [PATCH 46/79] fix unsoundness in Step::forward_unchecked for signed integers --- library/core/src/iter/range.rs | 30 ++++++++++++++++++++++++++++-- library/core/tests/iter/range.rs | 5 +++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 68937161e046a..49135704f27fa 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -184,8 +184,25 @@ pub trait Step: Clone + PartialOrd + Sized { } } -// These are still macro-generated because the integer literals resolve to different types. -macro_rules! step_identical_methods { +// Separate impls for signed ranges because the distance within a signed range can be larger +// than the signed::MAX value. Therefore `as` casting to the signed type would be incorrect. +macro_rules! step_signed_methods { + ($unsigned: ty) => { + #[inline] + unsafe fn forward_unchecked(start: Self, n: usize) -> Self { + // SAFETY: the caller has to guarantee that `start + n` doesn't overflow. + unsafe { start.checked_add_unsigned(n as $unsigned).unwrap_unchecked() } + } + + #[inline] + unsafe fn backward_unchecked(start: Self, n: usize) -> Self { + // SAFETY: the caller has to guarantee that `start - n` doesn't overflow. + unsafe { start.checked_sub_unsigned(n as $unsigned).unwrap_unchecked() } + } + }; +} + +macro_rules! step_unsigned_methods { () => { #[inline] unsafe fn forward_unchecked(start: Self, n: usize) -> Self { @@ -198,7 +215,12 @@ macro_rules! step_identical_methods { // SAFETY: the caller has to guarantee that `start - n` doesn't overflow. unsafe { start.unchecked_sub(n as Self) } } + }; +} +// These are still macro-generated because the integer literals resolve to different types. +macro_rules! step_identical_methods { + () => { #[inline] #[allow(arithmetic_overflow)] #[rustc_inherit_overflow_checks] @@ -239,6 +261,7 @@ macro_rules! step_integer_impls { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $u_narrower { step_identical_methods!(); + step_unsigned_methods!(); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -271,6 +294,7 @@ macro_rules! step_integer_impls { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $i_narrower { step_identical_methods!(); + step_signed_methods!($u_narrower); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -335,6 +359,7 @@ macro_rules! step_integer_impls { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $u_wider { step_identical_methods!(); + step_unsigned_methods!(); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -360,6 +385,7 @@ macro_rules! step_integer_impls { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $i_wider { step_identical_methods!(); + step_signed_methods!($u_wider); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs index 9af07119a89a2..e31db0732e094 100644 --- a/library/core/tests/iter/range.rs +++ b/library/core/tests/iter/range.rs @@ -325,6 +325,11 @@ fn test_range_advance_by() { assert_eq!(Ok(()), r.advance_back_by(usize::MAX)); assert_eq!((r.start, r.end), (0u128 + usize::MAX as u128, u128::MAX - usize::MAX as u128)); + + // issue 122420, Step::forward_unchecked was unsound for signed integers + let mut r = -128i8..127; + assert_eq!(Ok(()), r.advance_by(200)); + assert_eq!(r.next(), Some(72)); } #[test] From d3cab9f4d6c778da99185a4548ef7f0899f04766 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Thu, 14 Mar 2024 00:57:59 +0100 Subject: [PATCH 47/79] update virtual clock in miri test since signed loops now execute more methods --- src/tools/miri/tests/pass/shims/time-with-isolation2.stdout | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout b/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout index c68b40b744bf2..dce51a7fdbe0f 100644 --- a/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout +++ b/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout @@ -1 +1 @@ -The loop took around 7s +The loop took around 12s From 1a81a941ad62f6a619789eb67903121622488209 Mon Sep 17 00:00:00 2001 From: surechen Date: Mon, 11 Mar 2024 22:39:02 +0800 Subject: [PATCH 48/79] fixes #121331 --- compiler/rustc_lint_defs/src/builtin.rs | 1 + compiler/rustc_resolve/src/check_unused.rs | 60 +++++++++++++++++-- compiler/rustc_resolve/src/imports.rs | 11 ++-- compiler/rustc_resolve/src/late.rs | 29 +++++---- compiler/rustc_resolve/src/lib.rs | 7 ++- tests/ui/lint/lint-qualification.fixed | 7 ++- tests/ui/lint/lint-qualification.rs | 5 +- tests/ui/lint/lint-qualification.stderr | 30 +++++----- ...necessary-qualification-issue-121331.fixed | 50 ++++++++++++++++ ...-unnecessary-qualification-issue-121331.rs | 50 ++++++++++++++++ ...ecessary-qualification-issue-121331.stderr | 31 ++++++++++ ...lid-unused-qualifications-suggestion.fixed | 11 ++++ ...nvalid-unused-qualifications-suggestion.rs | 11 ++++ ...id-unused-qualifications-suggestion.stderr | 2 +- .../unused-qualifications-suggestion.fixed | 2 + .../unused-qualifications-suggestion.rs | 2 + .../unused-qualifications-suggestion.stderr | 2 +- 17 files changed, 269 insertions(+), 42 deletions(-) create mode 100644 tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed create mode 100644 tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs create mode 100644 tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index f2560b35aa2e1..c209f2772ff48 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -556,6 +556,7 @@ declare_lint! { /// fn main() { /// use foo::bar; /// foo::bar(); + /// bar(); /// } /// ``` /// diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index bf1ea2e270932..f6004fed8284e 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -23,18 +23,19 @@ // - `check_unused` finally emits the diagnostics based on the data generated // in the last step -use crate::imports::ImportKind; +use crate::imports::{Import, ImportKind}; use crate::module_to_string; use crate::Resolver; -use crate::NameBindingKind; +use crate::{LexicalScopeBinding, NameBindingKind}; use rustc_ast as ast; use rustc_ast::visit::{self, Visitor}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{pluralize, MultiSpan}; use rustc_hir::def::{DefKind, Res}; -use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS}; +use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES}; +use rustc_session::lint::builtin::{UNUSED_IMPORTS, UNUSED_QUALIFICATIONS}; use rustc_session::lint::BuiltinLintDiag; use rustc_span::symbol::{kw, Ident}; use rustc_span::{Span, DUMMY_SP}; @@ -514,8 +515,59 @@ impl Resolver<'_, '_> { } } + let mut redundant_imports = UnordSet::default(); for import in check_redundant_imports { - self.check_for_redundant_imports(import); + if self.check_for_redundant_imports(import) + && let Some(id) = import.id() + { + redundant_imports.insert(id); + } + } + + // The lint fixes for unused_import and unnecessary_qualification may conflict. + // Deleting both unused imports and unnecessary segments of an item may result + // in the item not being found. + for unn_qua in &self.potentially_unnecessary_qualifications { + if let LexicalScopeBinding::Item(name_binding) = unn_qua.binding + && let NameBindingKind::Import { import, .. } = name_binding.kind + && (is_unused_import(import, &unused_imports) + || is_redundant_import(import, &redundant_imports)) + { + continue; + } + + self.lint_buffer.buffer_lint_with_diagnostic( + UNUSED_QUALIFICATIONS, + unn_qua.node_id, + unn_qua.path_span, + "unnecessary qualification", + BuiltinLintDiag::UnusedQualifications { removal_span: unn_qua.removal_span }, + ); + } + + fn is_redundant_import( + import: Import<'_>, + redundant_imports: &UnordSet, + ) -> bool { + if let Some(id) = import.id() + && redundant_imports.contains(&id) + { + return true; + } + false + } + + fn is_unused_import( + import: Import<'_>, + unused_imports: &FxIndexMap, + ) -> bool { + if let Some(unused_import) = unused_imports.get(&import.root_id) + && let Some(id) = import.id() + && unused_import.unused.contains(&id) + { + return true; + } + false } } } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index ea08041f2aadd..9bf3e9ccabdf1 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1306,7 +1306,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { None } - pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'a>) { + pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'a>) -> bool { // This function is only called for single imports. let ImportKind::Single { source, target, ref source_bindings, ref target_bindings, id, .. @@ -1317,12 +1317,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Skip if the import is of the form `use source as target` and source != target. if source != target { - return; + return false; } // Skip if the import was produced by a macro. if import.parent_scope.expansion != LocalExpnId::ROOT { - return; + return false; } // Skip if we are inside a named module (in contrast to an anonymous @@ -1332,7 +1332,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { if import.used.get() == Some(Used::Other) || self.effective_visibilities.is_exported(self.local_def_id(id)) { - return; + return false; } let mut is_redundant = true; @@ -1375,7 +1375,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { format!("the item `{source}` is imported redundantly"), BuiltinLintDiag::RedundantImport(redundant_spans, source), ); + return true; } + + false } fn resolve_glob_import(&mut self, import: Import<'a>) { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index a996188db0229..c9b5c659f0f47 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -580,6 +580,15 @@ impl MaybeExported<'_> { } } +/// Used for recording UnnecessaryQualification. +#[derive(Debug)] +pub(crate) struct UnnecessaryQualification<'a> { + pub binding: LexicalScopeBinding<'a>, + pub node_id: NodeId, + pub path_span: Span, + pub removal_span: Span, +} + #[derive(Default)] struct DiagMetadata<'ast> { /// The current trait's associated items' ident, used for diagnostic suggestions. @@ -4654,20 +4663,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let ns = if i + 1 == path.len() { ns } else { TypeNS }; let res = self.r.partial_res_map.get(&seg.id?)?.full_res()?; let binding = self.resolve_ident_in_lexical_scope(seg.ident, ns, None, None)?; - - (res == binding.res()).then_some(seg) + (res == binding.res()).then_some((seg, binding)) }); - if let Some(unqualified) = unqualified { - self.r.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNUSED_QUALIFICATIONS, - finalize.node_id, - finalize.path_span, - "unnecessary qualification", - lint::BuiltinLintDiag::UnusedQualifications { - removal_span: path[0].ident.span.until(unqualified.ident.span), - }, - ); + if let Some((seg, binding)) = unqualified { + self.r.potentially_unnecessary_qualifications.push(UnnecessaryQualification { + binding, + node_id: finalize.node_id, + path_span: finalize.path_span, + removal_span: path[0].ident.span.until(seg.ident.span), + }); } } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index ccb67ea78cfdb..dfc2d029d4cfd 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -68,7 +68,7 @@ use std::fmt; use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use imports::{Import, ImportData, ImportKind, NameResolution}; -use late::{HasGenericParams, PathSource, PatternSource}; +use late::{HasGenericParams, PathSource, PatternSource, UnnecessaryQualification}; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use crate::effective_visibilities::EffectiveVisibilitiesVisitor; @@ -372,7 +372,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment { /// This refers to the thing referred by a name. The difference between `Res` and `Item` is that /// items are visible in their whole block, while `Res`es only from the place they are defined /// forward. -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] enum LexicalScopeBinding<'a> { Item(NameBinding<'a>), Res(Res), @@ -1105,6 +1105,8 @@ pub struct Resolver<'a, 'tcx> { potentially_unused_imports: Vec>, + potentially_unnecessary_qualifications: Vec>, + /// Table for mapping struct IDs into struct constructor IDs, /// it's not used during normal resolution, only for better error reporting. /// Also includes of list of each fields visibility @@ -1464,6 +1466,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { local_macro_def_scopes: FxHashMap::default(), name_already_seen: FxHashMap::default(), potentially_unused_imports: Vec::new(), + potentially_unnecessary_qualifications: Default::default(), struct_constructors: Default::default(), unused_macros: Default::default(), unused_macro_rules: Default::default(), diff --git a/tests/ui/lint/lint-qualification.fixed b/tests/ui/lint/lint-qualification.fixed index 2b1f8b591752a..6fe6ba2792fe6 100644 --- a/tests/ui/lint/lint-qualification.fixed +++ b/tests/ui/lint/lint-qualification.fixed @@ -1,5 +1,6 @@ //@ run-rustfix #![deny(unused_qualifications)] +#![deny(unused_imports)] #![allow(deprecated, dead_code)] mod foo { @@ -21,8 +22,10 @@ fn main() { //~^ ERROR: unnecessary qualification //~| ERROR: unnecessary qualification - use std::fmt; - let _: fmt::Result = Ok(()); //~ ERROR: unnecessary qualification + + //~^ ERROR: unused import: `std::fmt` + let _: std::fmt::Result = Ok(()); + // don't report unnecessary qualification because fix(#122373) for issue #121331 let _ = ::default(); // issue #121999 //~^ ERROR: unnecessary qualification diff --git a/tests/ui/lint/lint-qualification.rs b/tests/ui/lint/lint-qualification.rs index 002fdbf7724a7..19d339b006c9a 100644 --- a/tests/ui/lint/lint-qualification.rs +++ b/tests/ui/lint/lint-qualification.rs @@ -1,5 +1,6 @@ //@ run-rustfix #![deny(unused_qualifications)] +#![deny(unused_imports)] #![allow(deprecated, dead_code)] mod foo { @@ -22,7 +23,9 @@ fn main() { //~| ERROR: unnecessary qualification use std::fmt; - let _: std::fmt::Result = Ok(()); //~ ERROR: unnecessary qualification + //~^ ERROR: unused import: `std::fmt` + let _: std::fmt::Result = Ok(()); + // don't report unnecessary qualification because fix(#122373) for issue #121331 let _ = ::default(); // issue #121999 //~^ ERROR: unnecessary qualification diff --git a/tests/ui/lint/lint-qualification.stderr b/tests/ui/lint/lint-qualification.stderr index 8dddcf23f7575..9e5c9b2df13f1 100644 --- a/tests/ui/lint/lint-qualification.stderr +++ b/tests/ui/lint/lint-qualification.stderr @@ -1,5 +1,5 @@ error: unnecessary qualification - --> $DIR/lint-qualification.rs:11:5 + --> $DIR/lint-qualification.rs:12:5 | LL | foo::bar(); | ^^^^^^^^ @@ -16,7 +16,7 @@ LL + bar(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:12:5 + --> $DIR/lint-qualification.rs:13:5 | LL | crate::foo::bar(); | ^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL + bar(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:17:13 + --> $DIR/lint-qualification.rs:18:13 | LL | let _ = std::string::String::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL + let _ = String::new(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:18:13 + --> $DIR/lint-qualification.rs:19:13 | LL | let _ = ::std::env::current_dir(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + let _ = std::env::current_dir(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:20:12 + --> $DIR/lint-qualification.rs:21:12 | LL | let _: std::vec::Vec = std::vec::Vec::::new(); | ^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL + let _: Vec = std::vec::Vec::::new(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:20:36 + --> $DIR/lint-qualification.rs:21:36 | LL | let _: std::vec::Vec = std::vec::Vec::::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,20 +75,20 @@ LL - let _: std::vec::Vec = std::vec::Vec::::new(); LL + let _: std::vec::Vec = Vec::::new(); | -error: unnecessary qualification - --> $DIR/lint-qualification.rs:25:12 - | -LL | let _: std::fmt::Result = Ok(()); - | ^^^^^^^^^^^^^^^^ +error: unused import: `std::fmt` + --> $DIR/lint-qualification.rs:25:9 | -help: remove the unnecessary path segments +LL | use std::fmt; + | ^^^^^^^^ | -LL - let _: std::fmt::Result = Ok(()); -LL + let _: fmt::Result = Ok(()); +note: the lint level is defined here + --> $DIR/lint-qualification.rs:3:9 | +LL | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ error: unnecessary qualification - --> $DIR/lint-qualification.rs:27:13 + --> $DIR/lint-qualification.rs:30:13 | LL | let _ = ::default(); // issue #121999 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed new file mode 100644 index 0000000000000..d554bbfcc98c9 --- /dev/null +++ b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed @@ -0,0 +1,50 @@ +//@ run-rustfix +//@ edition:2021 +#![deny(unused_qualifications)] +#![deny(unused_imports)] +#![feature(coroutines, coroutine_trait)] + +use std::ops::{ + Coroutine, + CoroutineState::{self}, + //~^ ERROR unused import: `*` +}; +use std::pin::Pin; + +#[allow(dead_code)] +fn finish(mut amt: usize, mut t: T) -> T::Return + where T: Coroutine<(), Yield = ()> + Unpin, +{ + loop { + match Pin::new(&mut t).resume(()) { + CoroutineState::Yielded(()) => amt = amt.checked_sub(1).unwrap(), + CoroutineState::Complete(ret) => { + assert_eq!(amt, 0); + return ret + } + } + } +} + + +mod foo { + pub fn bar() {} +} + +pub fn main() { + + use foo::bar; + bar(); + //~^ ERROR unnecessary qualification + bar(); + + // The item `use std::string::String` is imported redundantly. + // Suppress `unused_imports` reporting, otherwise the fixed file will report an error + #[allow(unused_imports)] + use std::string::String; + let s = String::new(); + let y = std::string::String::new(); + // unnecessary qualification + println!("{} {}", s, y); + +} diff --git a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs new file mode 100644 index 0000000000000..4d79f5ab74530 --- /dev/null +++ b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs @@ -0,0 +1,50 @@ +//@ run-rustfix +//@ edition:2021 +#![deny(unused_qualifications)] +#![deny(unused_imports)] +#![feature(coroutines, coroutine_trait)] + +use std::ops::{ + Coroutine, + CoroutineState::{self, *}, + //~^ ERROR unused import: `*` +}; +use std::pin::Pin; + +#[allow(dead_code)] +fn finish(mut amt: usize, mut t: T) -> T::Return + where T: Coroutine<(), Yield = ()> + Unpin, +{ + loop { + match Pin::new(&mut t).resume(()) { + CoroutineState::Yielded(()) => amt = amt.checked_sub(1).unwrap(), + CoroutineState::Complete(ret) => { + assert_eq!(amt, 0); + return ret + } + } + } +} + + +mod foo { + pub fn bar() {} +} + +pub fn main() { + + use foo::bar; + foo::bar(); + //~^ ERROR unnecessary qualification + bar(); + + // The item `use std::string::String` is imported redundantly. + // Suppress `unused_imports` reporting, otherwise the fixed file will report an error + #[allow(unused_imports)] + use std::string::String; + let s = String::new(); + let y = std::string::String::new(); + // unnecessary qualification + println!("{} {}", s, y); + +} diff --git a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.stderr b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.stderr new file mode 100644 index 0000000000000..52ed13ea150d7 --- /dev/null +++ b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.stderr @@ -0,0 +1,31 @@ +error: unused import: `*` + --> $DIR/lint-unnecessary-qualification-issue-121331.rs:9:28 + | +LL | CoroutineState::{self, *}, + | ^ + | +note: the lint level is defined here + --> $DIR/lint-unnecessary-qualification-issue-121331.rs:4:9 + | +LL | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ + +error: unnecessary qualification + --> $DIR/lint-unnecessary-qualification-issue-121331.rs:37:5 + | +LL | foo::bar(); + | ^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-unnecessary-qualification-issue-121331.rs:3:9 + | +LL | #![deny(unused_qualifications)] + | ^^^^^^^^^^^^^^^^^^^^^ +help: remove the unnecessary path segments + | +LL - foo::bar(); +LL + bar(); + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed index 8a67b20eec11d..d95faef8ac44b 100644 --- a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed +++ b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed @@ -18,6 +18,16 @@ impl Index for A { } } +// This is used to make `use std::ops::Index;` not unused_import. +// details in fix(#122373) for issue #121331 +pub struct C; +impl Index for C { + type Output = (); + fn index(&self, _: str) -> &Self::Output { + &() + } +} + mod inner { pub trait Trait {} } @@ -29,4 +39,5 @@ use inner::Trait; impl Trait for () {} //~^ ERROR unnecessary qualification +impl Trait for A {} fn main() {} diff --git a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs index 528edb331cf55..0eee8f71ad4e1 100644 --- a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs +++ b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs @@ -18,6 +18,16 @@ impl ops::Index for A { } } +// This is used to make `use std::ops::Index;` not unused_import. +// details in fix(#122373) for issue #121331 +pub struct C; +impl Index for C { + type Output = (); + fn index(&self, _: str) -> &Self::Output { + &() + } +} + mod inner { pub trait Trait {} } @@ -29,4 +39,5 @@ use inner::Trait; impl inner::Trait for () {} //~^ ERROR unnecessary qualification +impl Trait for A {} fn main() {} diff --git a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr index bcda721071235..e105b754b7195 100644 --- a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr +++ b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr @@ -16,7 +16,7 @@ LL + impl Index for A { | error: unnecessary qualification - --> $DIR/issue-113808-invalid-unused-qualifications-suggestion.rs:29:6 + --> $DIR/issue-113808-invalid-unused-qualifications-suggestion.rs:39:6 | LL | impl inner::Trait for () {} | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/resolve/unused-qualifications-suggestion.fixed b/tests/ui/resolve/unused-qualifications-suggestion.fixed index 6935f611b361f..22e0daea46764 100644 --- a/tests/ui/resolve/unused-qualifications-suggestion.fixed +++ b/tests/ui/resolve/unused-qualifications-suggestion.fixed @@ -16,8 +16,10 @@ fn main() { use foo::bar; bar(); //~^ ERROR unnecessary qualification + bar(); use baz::qux::quux; quux(); //~^ ERROR unnecessary qualification + quux(); } diff --git a/tests/ui/resolve/unused-qualifications-suggestion.rs b/tests/ui/resolve/unused-qualifications-suggestion.rs index b3fe04ff0ea4b..89516c1344a9e 100644 --- a/tests/ui/resolve/unused-qualifications-suggestion.rs +++ b/tests/ui/resolve/unused-qualifications-suggestion.rs @@ -16,8 +16,10 @@ fn main() { use foo::bar; foo::bar(); //~^ ERROR unnecessary qualification + bar(); use baz::qux::quux; baz::qux::quux(); //~^ ERROR unnecessary qualification + quux(); } diff --git a/tests/ui/resolve/unused-qualifications-suggestion.stderr b/tests/ui/resolve/unused-qualifications-suggestion.stderr index e3dac37fc6e22..5b71ba9e2223c 100644 --- a/tests/ui/resolve/unused-qualifications-suggestion.stderr +++ b/tests/ui/resolve/unused-qualifications-suggestion.stderr @@ -16,7 +16,7 @@ LL + bar(); | error: unnecessary qualification - --> $DIR/unused-qualifications-suggestion.rs:21:5 + --> $DIR/unused-qualifications-suggestion.rs:22:5 | LL | baz::qux::quux(); | ^^^^^^^^^^^^^^ From f9cdaeb6fdbfc91705bb790d892219db544a787c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 8 Feb 2024 12:41:44 +1100 Subject: [PATCH 49/79] coverage: Data structures for recording branch info during MIR building --- compiler/rustc_middle/src/mir/coverage.rs | 23 +++++++++++-- compiler/rustc_middle/src/mir/mod.rs | 8 +++++ compiler/rustc_middle/src/mir/pretty.rs | 22 +++++++++++++ .../rustc_mir_build/src/build/coverageinfo.rs | 32 +++++++++++++++++++ .../rustc_mir_build/src/build/custom/mod.rs | 1 + compiler/rustc_mir_build/src/build/mod.rs | 12 +++++-- 6 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 compiler/rustc_mir_build/src/build/coverageinfo.rs diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index ed3a73af1ce27..2c51aec3a7609 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -2,7 +2,7 @@ use rustc_index::IndexVec; use rustc_macros::HashStable; -use rustc_span::Symbol; +use rustc_span::{Span, Symbol}; use std::fmt::{self, Debug, Formatter}; @@ -93,7 +93,7 @@ pub enum CoverageKind { SpanMarker, /// Marks its enclosing basic block with an ID that can be referred to by - /// other data in the MIR body. + /// side data in [`BranchInfo`]. /// /// Has no effect during codegen. BlockMarker { id: BlockMarkerId }, @@ -218,3 +218,22 @@ pub struct FunctionCoverageInfo { pub expressions: IndexVec, pub mappings: Vec, } + +/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR. +#[derive(Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct BranchInfo { + /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was + /// injected into the MIR body. This makes it possible to allocate per-ID + /// data structures without having to scan the entire body first. + pub num_block_markers: usize, + pub branch_spans: Vec, +} + +#[derive(Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct BranchSpan { + pub span: Span, + pub true_marker: BlockMarkerId, + pub false_marker: BlockMarkerId, +} diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index b71c614dc4fe9..d57ffc0f8b5c5 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -403,6 +403,12 @@ pub struct Body<'tcx> { pub tainted_by_errors: Option, + /// Branch coverage information collected during MIR building, to be used by + /// the `InstrumentCoverage` pass. + /// + /// Only present if branch coverage is enabled and this function is eligible. + pub coverage_branch_info: Option>, + /// Per-function coverage information added by the `InstrumentCoverage` /// pass, to be used in conjunction with the coverage statements injected /// into this body's blocks. @@ -450,6 +456,7 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, injection_phase: None, tainted_by_errors, + coverage_branch_info: None, function_coverage_info: None, }; body.is_polymorphic = body.has_non_region_param(); @@ -479,6 +486,7 @@ impl<'tcx> Body<'tcx> { is_polymorphic: false, injection_phase: None, tainted_by_errors: None, + coverage_branch_info: None, function_coverage_info: None, }; body.is_polymorphic = body.has_non_region_param(); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8ae65f3832fd7..94751c4476157 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -461,6 +461,9 @@ pub fn write_mir_intro<'tcx>( // Add an empty line before the first block is printed. writeln!(w)?; + if let Some(branch_info) = &body.coverage_branch_info { + write_coverage_branch_info(branch_info, w)?; + } if let Some(function_coverage_info) = &body.function_coverage_info { write_function_coverage_info(function_coverage_info, w)?; } @@ -468,6 +471,25 @@ pub fn write_mir_intro<'tcx>( Ok(()) } +fn write_coverage_branch_info( + branch_info: &coverage::BranchInfo, + w: &mut dyn io::Write, +) -> io::Result<()> { + let coverage::BranchInfo { branch_spans, .. } = branch_info; + + for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans { + writeln!( + w, + "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", + )?; + } + if !branch_spans.is_empty() { + writeln!(w)?; + } + + Ok(()) +} + fn write_function_coverage_info( function_coverage_info: &coverage::FunctionCoverageInfo, w: &mut dyn io::Write, diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs new file mode 100644 index 0000000000000..3fdf48f54c983 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -0,0 +1,32 @@ +use rustc_middle::mir; +use rustc_middle::mir::coverage::BranchSpan; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::LocalDefId; + +pub(crate) struct BranchInfoBuilder { + num_block_markers: usize, + branch_spans: Vec, +} + +impl BranchInfoBuilder { + /// Creates a new branch info builder, but only if branch coverage instrumentation + /// is enabled and `def_id` represents a function that is eligible for coverage. + pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { + if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) { + Some(Self { num_block_markers: 0, branch_spans: vec![] }) + } else { + None + } + } + + pub(crate) fn into_done(self) -> Option> { + let Self { num_block_markers, branch_spans } = self; + + if num_block_markers == 0 { + assert!(branch_spans.is_empty()); + return None; + } + + Some(Box::new(mir::coverage::BranchInfo { num_block_markers, branch_spans })) + } +} diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index c2bff9084c67e..288b787798b49 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -60,6 +60,7 @@ pub(super) fn build_custom_mir<'tcx>( tainted_by_errors: None, injection_phase: None, pass_count: 0, + coverage_branch_info: None, function_coverage_info: None, }; diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 45954bdb114f2..2fe5d3fb5e0df 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -234,6 +234,10 @@ struct Builder<'a, 'tcx> { // the root (most of them do) and saves us from retracing many sub-paths // many times, and rechecking many nodes. lint_level_roots_cache: GrowableBitSet, + + /// Collects additional coverage information during MIR building. + /// Only present if branch coverage is enabled and this function is eligible. + coverage_branch_info: Option, } type CaptureMap<'tcx> = SortedIndexMultiMap>; @@ -807,6 +811,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unit_temp: None, var_debug_info: vec![], lint_level_roots_cache: GrowableBitSet::new_empty(), + coverage_branch_info: coverageinfo::BranchInfoBuilder::new_if_enabled(tcx, def), }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); @@ -826,7 +831,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - Body::new( + let mut body = Body::new( MirSource::item(self.def_id.to_def_id()), self.cfg.basic_blocks, self.source_scopes, @@ -837,7 +842,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.fn_span, self.coroutine, None, - ) + ); + body.coverage_branch_info = self.coverage_branch_info.and_then(|b| b.into_done()); + body } fn insert_upvar_arg(&mut self) { @@ -1111,6 +1118,7 @@ pub(crate) fn parse_float_into_scalar( mod block; mod cfg; +mod coverageinfo; mod custom; mod expr; mod matches; From c1bec0ce6b6eefabd153c315ccec4dfce3808885 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 29 Nov 2023 15:48:50 +1100 Subject: [PATCH 50/79] coverage: Record branch information during MIR building --- .../rustc_mir_build/src/build/coverageinfo.rs | 124 +++++++++++++++++- .../rustc_mir_build/src/build/matches/mod.rs | 11 ++ 2 files changed, 131 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index 3fdf48f54c983..0b8ec234dda7a 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -1,26 +1,92 @@ -use rustc_middle::mir; -use rustc_middle::mir::coverage::BranchSpan; +use std::assert_matches::assert_matches; +use std::collections::hash_map::Entry; + +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; +use rustc_middle::mir::{self, BasicBlock, UnOp}; +use rustc_middle::thir::{ExprId, ExprKind, Thir}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::LocalDefId; +use crate::build::Builder; + pub(crate) struct BranchInfoBuilder { + /// Maps condition expressions to their enclosing `!`, for better instrumentation. + nots: FxHashMap, + num_block_markers: usize, branch_spans: Vec, } +#[derive(Clone, Copy)] +struct NotInfo { + /// When visiting the associated expression as a branch condition, treat this + /// enclosing `!` as the branch condition instead. + enclosing_not: ExprId, + /// True if the associated expression is nested within an odd number of `!` + /// expressions relative to `enclosing_not` (inclusive of `enclosing_not`). + is_flipped: bool, +} + impl BranchInfoBuilder { /// Creates a new branch info builder, but only if branch coverage instrumentation /// is enabled and `def_id` represents a function that is eligible for coverage. pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) { - Some(Self { num_block_markers: 0, branch_spans: vec![] }) + Some(Self { nots: FxHashMap::default(), num_block_markers: 0, branch_spans: vec![] }) } else { None } } + /// Unary `!` expressions inside an `if` condition are lowered by lowering + /// their argument instead, and then reversing the then/else arms of that `if`. + /// + /// That's awkward for branch coverage instrumentation, so to work around that + /// we pre-emptively visit any affected `!` expressions, and record extra + /// information that [`Builder::visit_coverage_branch_condition`] can use to + /// synthesize branch instrumentation for the enclosing `!`. + pub(crate) fn visit_unary_not(&mut self, thir: &Thir<'_>, unary_not: ExprId) { + assert_matches!(thir[unary_not].kind, ExprKind::Unary { op: UnOp::Not, .. }); + + self.visit_with_not_info( + thir, + unary_not, + // Set `is_flipped: false` for the `!` itself, so that its enclosed + // expression will have `is_flipped: true`. + NotInfo { enclosing_not: unary_not, is_flipped: false }, + ); + } + + fn visit_with_not_info(&mut self, thir: &Thir<'_>, expr_id: ExprId, not_info: NotInfo) { + match self.nots.entry(expr_id) { + // This expression has already been marked by an enclosing `!`. + Entry::Occupied(_) => return, + Entry::Vacant(entry) => entry.insert(not_info), + }; + + match thir[expr_id].kind { + ExprKind::Unary { op: UnOp::Not, arg } => { + // Invert the `is_flipped` flag for the contents of this `!`. + let not_info = NotInfo { is_flipped: !not_info.is_flipped, ..not_info }; + self.visit_with_not_info(thir, arg, not_info); + } + ExprKind::Scope { value, .. } => self.visit_with_not_info(thir, value, not_info), + ExprKind::Use { source } => self.visit_with_not_info(thir, source, not_info), + // All other expressions (including `&&` and `||`) don't need any + // special handling of their contents, so stop visiting. + _ => {} + } + } + + fn next_block_marker_id(&mut self) -> BlockMarkerId { + let id = BlockMarkerId::from_usize(self.num_block_markers); + self.num_block_markers += 1; + id + } + pub(crate) fn into_done(self) -> Option> { - let Self { num_block_markers, branch_spans } = self; + let Self { nots: _, num_block_markers, branch_spans } = self; if num_block_markers == 0 { assert!(branch_spans.is_empty()); @@ -30,3 +96,53 @@ impl BranchInfoBuilder { Some(Box::new(mir::coverage::BranchInfo { num_block_markers, branch_spans })) } } + +impl Builder<'_, '_> { + /// If branch coverage is enabled, inject marker statements into `then_block` + /// and `else_block`, and record their IDs in the table of branch spans. + pub(crate) fn visit_coverage_branch_condition( + &mut self, + mut expr_id: ExprId, + mut then_block: BasicBlock, + mut else_block: BasicBlock, + ) { + // Bail out if branch coverage is not enabled for this function. + let Some(branch_info) = self.coverage_branch_info.as_ref() else { return }; + + // If this condition expression is nested within one or more `!` expressions, + // replace it with the enclosing `!` collected by `visit_unary_not`. + if let Some(&NotInfo { enclosing_not, is_flipped }) = branch_info.nots.get(&expr_id) { + expr_id = enclosing_not; + if is_flipped { + std::mem::swap(&mut then_block, &mut else_block); + } + } + let source_info = self.source_info(self.thir[expr_id].span); + + // Now that we have `source_info`, we can upgrade to a &mut reference. + let branch_info = self.coverage_branch_info.as_mut().expect("upgrading & to &mut"); + + let mut inject_branch_marker = |block: BasicBlock| { + let id = branch_info.next_block_marker_id(); + + let marker_statement = mir::Statement { + source_info, + kind: mir::StatementKind::Coverage(Box::new(mir::Coverage { + kind: CoverageKind::BlockMarker { id }, + })), + }; + self.cfg.push(block, marker_statement); + + id + }; + + let true_marker = inject_branch_marker(then_block); + let false_marker = inject_branch_marker(else_block); + + branch_info.branch_spans.push(BranchSpan { + span: source_info.span, + true_marker, + false_marker, + }); + } +} diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 4d5ed65c8416c..e7808ff880b57 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -105,6 +105,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block.unit() } ExprKind::Unary { op: UnOp::Not, arg } => { + // Improve branch coverage instrumentation by noting conditions + // nested within one or more `!` expressions. + // (Skipped if branch coverage is not enabled.) + if let Some(branch_info) = this.coverage_branch_info.as_mut() { + branch_info.visit_unary_not(this.thir, expr_id); + } + let local_scope = this.local_scope(); let (success_block, failure_block) = this.in_if_then_scope(local_scope, expr_span, |this| { @@ -149,6 +156,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let else_block = this.cfg.start_new_block(); let term = TerminatorKind::if_(operand, then_block, else_block); + // Record branch coverage info for this condition. + // (Does nothing if branch coverage is not enabled.) + this.visit_coverage_branch_condition(expr_id, then_block, else_block); + let source_info = this.source_info(expr_span); this.cfg.terminate(block, source_info, term); this.break_for_else(else_block, source_info); From 31d0b5017857cef35031a7d4210bd045758dcae2 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Thu, 16 Nov 2023 17:48:23 +1100 Subject: [PATCH 51/79] coverage: Include recorded branch info in coverage instrumentation --- .../src/coverageinfo/ffi.rs | 12 +++- compiler/rustc_middle/src/mir/coverage.rs | 9 ++- .../rustc_mir_transform/src/coverage/mod.rs | 4 ++ .../rustc_mir_transform/src/coverage/spans.rs | 12 ++++ .../src/coverage/spans/from_mir.rs | 55 ++++++++++++++++++- 5 files changed, 85 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 017843c7e7d15..2af28146a51ea 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -164,6 +164,15 @@ impl CounterMappingRegion { end_line, end_col, ), + MappingKind::Branch { true_term, false_term } => Self::branch_region( + Counter::from_term(true_term), + Counter::from_term(false_term), + local_file_id, + start_line, + start_col, + end_line, + end_col, + ), } } @@ -188,9 +197,6 @@ impl CounterMappingRegion { } } - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] pub(crate) fn branch_region( counter: Counter, false_counter: Counter, diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 2c51aec3a7609..645a417c32250 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -179,14 +179,18 @@ pub struct Expression { pub enum MappingKind { /// Associates a normal region of code with a counter/expression/zero. Code(CovTerm), + /// Associates a branch region with separate counters for true and false. + Branch { true_term: CovTerm, false_term: CovTerm }, } impl MappingKind { /// Iterator over all coverage terms in this mapping kind. pub fn terms(&self) -> impl Iterator { - let one = |a| std::iter::once(a); + let one = |a| std::iter::once(a).chain(None); + let two = |a, b| std::iter::once(a).chain(Some(b)); match *self { Self::Code(term) => one(term), + Self::Branch { true_term, false_term } => two(true_term, false_term), } } @@ -195,6 +199,9 @@ impl MappingKind { pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self { match *self { Self::Code(term) => Self::Code(map_fn(term)), + Self::Branch { true_term, false_term } => { + Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) } + } } } } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index bde135834860b..b2407c545071b 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -139,6 +139,10 @@ fn create_mappings<'tcx>( .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| { let kind = match bcb_mapping_kind { BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), + BcbMappingKind::Branch { true_bcb, false_bcb } => MappingKind::Branch { + true_term: term_for_bcb(true_bcb), + false_term: term_for_bcb(false_bcb), + }, }; let code_region = make_code_region(source_map, file_name, span, body_span)?; Some(Mapping { kind, code_region }) diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 4260a6f0c6f79..672de1fbe60fd 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -13,6 +13,8 @@ mod from_mir; pub(super) enum BcbMappingKind { /// Associates an ordinary executable code span with its corresponding BCB. Code(BasicCoverageBlock), + /// Associates a branch span with BCBs for its true and false arms. + Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock }, } #[derive(Debug)] @@ -66,6 +68,12 @@ pub(super) fn generate_coverage_spans( // Each span produced by the generator represents an ordinary code region. BcbMapping { kind: BcbMappingKind::Code(bcb), span } })); + + mappings.extend(from_mir::extract_branch_mappings( + mir_body, + hir_info.body_span, + basic_coverage_blocks, + )); } if mappings.is_empty() { @@ -80,6 +88,10 @@ pub(super) fn generate_coverage_spans( for &BcbMapping { kind, span: _ } in &mappings { match kind { BcbMappingKind::Code(bcb) => insert(bcb), + BcbMappingKind::Branch { true_bcb, false_bcb } => { + insert(true_bcb); + insert(false_bcb); + } } } diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 223613cfc6fdf..86097bdcd9537 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,7 +1,9 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; +use rustc_index::IndexVec; +use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; use rustc_middle::mir::{ - self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, + self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; @@ -9,6 +11,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; use crate::coverage::graph::{ BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, }; +use crate::coverage::spans::{BcbMapping, BcbMappingKind}; use crate::coverage::ExtractedHirInfo; /// Traverses the MIR body to produce an initial collection of coverage-relevant @@ -179,8 +182,6 @@ fn is_closure_like(statement: &Statement<'_>) -> bool { /// If the MIR `Statement` has a span contributive to computing coverage spans, /// return it; otherwise return `None`. fn filtered_statement_span(statement: &Statement<'_>) -> Option { - use mir::coverage::CoverageKind; - match statement.kind { // These statements have spans that are often outside the scope of the executed source code // for their parent `BasicBlock`. @@ -363,3 +364,51 @@ impl SpanFromMir { Self { span, visible_macro, bcb, is_hole } } } + +pub(super) fn extract_branch_mappings( + mir_body: &mir::Body<'_>, + body_span: Span, + basic_coverage_blocks: &CoverageGraph, +) -> Vec { + let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { + return vec![]; + }; + + let mut block_markers = IndexVec::>::from_elem_n( + None, + branch_info.num_block_markers, + ); + + // Fill out the mapping from block marker IDs to their enclosing blocks. + for (bb, data) in mir_body.basic_blocks.iter_enumerated() { + for statement in &data.statements { + if let StatementKind::Coverage(coverage) = &statement.kind + && let CoverageKind::BlockMarker { id } = coverage.kind + { + block_markers[id] = Some(bb); + } + } + } + + branch_info + .branch_spans + .iter() + .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| { + // For now, ignore any branch span that was introduced by + // expansion. This makes things like assert macros less noisy. + if !raw_span.ctxt().outer_expn_data().is_root() { + return None; + } + let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?; + + let bcb_from_marker = |marker: BlockMarkerId| { + Some(basic_coverage_blocks.bcb_from_bb(block_markers[marker]?)?) + }; + + let true_bcb = bcb_from_marker(true_marker)?; + let false_bcb = bcb_from_marker(false_marker)?; + + Some(BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span }) + }) + .collect::>() +} From 5fb1f61a7702d87380026e9f1c7a1fac01920d18 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 10 Mar 2024 21:20:27 +1100 Subject: [PATCH 52/79] coverage: Enable branch coverage in the branch coverage tests --- tests/coverage/branch_generics.cov-map | 27 ++-- tests/coverage/branch_generics.coverage | 16 +- tests/coverage/branch_generics.rs | 2 +- tests/coverage/branch_guard.cov-map | 26 ++-- tests/coverage/branch_guard.coverage | 8 +- tests/coverage/branch_guard.rs | 2 +- tests/coverage/branch_if.cov-map | 186 +++++++++++++++--------- tests/coverage/branch_if.coverage | 31 +++- tests/coverage/branch_if.rs | 2 +- tests/coverage/branch_while.cov-map | 70 ++++++--- tests/coverage/branch_while.coverage | 16 +- tests/coverage/branch_while.rs | 2 +- 12 files changed, 272 insertions(+), 116 deletions(-) diff --git a/tests/coverage/branch_generics.cov-map b/tests/coverage/branch_generics.cov-map index ff8bb632a54f6..719e97efad455 100644 --- a/tests/coverage/branch_generics.cov-map +++ b/tests/coverage/branch_generics.cov-map @@ -1,43 +1,52 @@ Function name: branch_generics::print_size::<()> -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 06, 01, 01, 24, 05, 01, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 2 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -Number of file 0 mappings: 4 +Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) -- Code(Counter(1)) at (prev + 1, 37) to (start + 2, 6) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 8) to (start + 0, 36) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 37) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) = (c0 - c1) - Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) = (c1 + (c0 - c1)) Function name: branch_generics::print_size:: -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 06, 01, 01, 24, 05, 01, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 2 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -Number of file 0 mappings: 4 +Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) -- Code(Counter(1)) at (prev + 1, 37) to (start + 2, 6) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 8) to (start + 0, 36) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 37) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) = (c0 - c1) - Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) = (c1 + (c0 - c1)) Function name: branch_generics::print_size:: -Raw bytes (28): 0x[01, 01, 02, 01, 05, 05, 02, 04, 01, 06, 01, 01, 24, 05, 01, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 2 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) - expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) -Number of file 0 mappings: 4 +Number of file 0 mappings: 5 - Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) -- Code(Counter(1)) at (prev + 1, 37) to (start + 2, 6) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 8) to (start + 0, 36) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 37) to (start + 2, 6) - Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) = (c0 - c1) - Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) diff --git a/tests/coverage/branch_generics.coverage b/tests/coverage/branch_generics.coverage index cfbd2d3f4bd46..e7cec151ce62c 100644 --- a/tests/coverage/branch_generics.coverage +++ b/tests/coverage/branch_generics.coverage @@ -1,10 +1,15 @@ LL| |#![feature(coverage_attribute)] LL| |//@ edition: 2021 - LL| | + LL| |//@ compile-flags: -Zcoverage-options=branch LL| |//@ llvm-cov-flags: --show-branches=count LL| | LL| 3|fn print_size() { LL| 3| if std::mem::size_of::() > 4 { + ------------------ + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:8): [True: 1, False: 0] + ------------------ LL| 1| println!("size > 4"); LL| 2| } else { LL| 2| println!("size <= 4"); @@ -14,6 +19,9 @@ | branch_generics::print_size::<()>: | LL| 1|fn print_size() { | LL| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (LL:8): [True: 0, False: 1] + | ------------------ | LL| 0| println!("size > 4"); | LL| 1| } else { | LL| 1| println!("size <= 4"); @@ -23,6 +31,9 @@ | branch_generics::print_size::: | LL| 1|fn print_size() { | LL| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (LL:8): [True: 0, False: 1] + | ------------------ | LL| 0| println!("size > 4"); | LL| 1| } else { | LL| 1| println!("size <= 4"); @@ -32,6 +43,9 @@ | branch_generics::print_size::: | LL| 1|fn print_size() { | LL| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (LL:8): [True: 1, False: 0] + | ------------------ | LL| 1| println!("size > 4"); | LL| 1| } else { | LL| 0| println!("size <= 4"); diff --git a/tests/coverage/branch_generics.rs b/tests/coverage/branch_generics.rs index ad1f5be33c4b1..d870ace7006b3 100644 --- a/tests/coverage/branch_generics.rs +++ b/tests/coverage/branch_generics.rs @@ -1,6 +1,6 @@ #![feature(coverage_attribute)] //@ edition: 2021 - +//@ compile-flags: -Zcoverage-options=branch //@ llvm-cov-flags: --show-branches=count fn print_size() { diff --git a/tests/coverage/branch_guard.cov-map b/tests/coverage/branch_guard.cov-map index e0cbbf491960f..0b3622f6347a8 100644 --- a/tests/coverage/branch_guard.cov-map +++ b/tests/coverage/branch_guard.cov-map @@ -1,24 +1,32 @@ Function name: branch_guard::branch_match_guard -Raw bytes (67): 0x[01, 01, 04, 05, 09, 0b, 15, 0f, 11, 03, 0d, 0b, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 15, 01, 14, 02, 0a, 0d, 03, 0e, 00, 0f, 19, 00, 14, 00, 19, 0d, 00, 1d, 02, 0a, 11, 03, 0e, 00, 0f, 1d, 00, 14, 00, 19, 11, 00, 1d, 02, 0a, 03, 03, 0e, 02, 0a, 07, 04, 01, 00, 02] +Raw bytes (85): 0x[01, 01, 06, 19, 0d, 05, 09, 0f, 15, 13, 11, 17, 0d, 05, 09, 0d, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 15, 01, 14, 02, 0a, 0d, 03, 0e, 00, 0f, 19, 00, 14, 00, 19, 20, 0d, 02, 00, 14, 00, 1e, 0d, 00, 1d, 02, 0a, 11, 03, 0e, 00, 0f, 1d, 00, 14, 00, 19, 20, 11, 09, 00, 14, 00, 1e, 11, 00, 1d, 02, 0a, 17, 03, 0e, 02, 0a, 0b, 04, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 4 -- expression 0 operands: lhs = Counter(1), rhs = Counter(2) -- expression 1 operands: lhs = Expression(2, Add), rhs = Counter(5) -- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(4) -- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(3) -Number of file 0 mappings: 11 +Number of expressions: 6 +- expression 0 operands: lhs = Counter(6), rhs = Counter(3) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(5) +- expression 3 operands: lhs = Expression(4, Add), rhs = Counter(4) +- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(3) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 13 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Code(Counter(7)) at (prev + 3, 11) to (start + 0, 12) - Code(Counter(5)) at (prev + 1, 20) to (start + 2, 10) - Code(Counter(3)) at (prev + 3, 14) to (start + 0, 15) - Code(Counter(6)) at (prev + 0, 20) to (start + 0, 25) +- Branch { true: Counter(3), false: Expression(0, Sub) } at (prev + 0, 20) to (start + 0, 30) + true = c3 + false = (c6 - c3) - Code(Counter(3)) at (prev + 0, 29) to (start + 2, 10) - Code(Counter(4)) at (prev + 3, 14) to (start + 0, 15) - Code(Counter(7)) at (prev + 0, 20) to (start + 0, 25) +- Branch { true: Counter(4), false: Counter(2) } at (prev + 0, 20) to (start + 0, 30) + true = c4 + false = c2 - Code(Counter(4)) at (prev + 0, 29) to (start + 2, 10) -- Code(Expression(0, Add)) at (prev + 3, 14) to (start + 2, 10) +- Code(Expression(5, Add)) at (prev + 3, 14) to (start + 2, 10) = (c1 + c2) -- Code(Expression(1, Add)) at (prev + 4, 1) to (start + 0, 2) +- Code(Expression(2, Add)) at (prev + 4, 1) to (start + 0, 2) = ((((c1 + c2) + c3) + c4) + c5) diff --git a/tests/coverage/branch_guard.coverage b/tests/coverage/branch_guard.coverage index 6156ae88c7461..f89b965b5d0f7 100644 --- a/tests/coverage/branch_guard.coverage +++ b/tests/coverage/branch_guard.coverage @@ -1,6 +1,6 @@ LL| |#![feature(coverage_attribute)] LL| |//@ edition: 2021 - LL| | + LL| |//@ compile-flags: -Zcoverage-options=branch LL| |//@ llvm-cov-flags: --show-branches=count LL| | LL| |macro_rules! no_merge { @@ -18,9 +18,15 @@ LL| 1| } LL| 3| Some(x) if x % 2 == 0 => { ^2 + ------------------ + | Branch (LL:20): [True: 2, False: 1] + ------------------ LL| 2| println!("is nonzero and even"); LL| 2| } LL| 1| Some(x) if x % 3 == 0 => { + ------------------ + | Branch (LL:20): [True: 1, False: 0] + ------------------ LL| 1| println!("is nonzero and odd, but divisible by 3"); LL| 1| } LL| 0| _ => { diff --git a/tests/coverage/branch_guard.rs b/tests/coverage/branch_guard.rs index a7cb389227e81..fa049e6206dc5 100644 --- a/tests/coverage/branch_guard.rs +++ b/tests/coverage/branch_guard.rs @@ -1,6 +1,6 @@ #![feature(coverage_attribute)] //@ edition: 2021 - +//@ compile-flags: -Zcoverage-options=branch //@ llvm-cov-flags: --show-branches=count macro_rules! no_merge { diff --git a/tests/coverage/branch_if.cov-map b/tests/coverage/branch_if.cov-map index 6fb5ef767148f..0dbfd92541b03 100644 --- a/tests/coverage/branch_if.cov-map +++ b/tests/coverage/branch_if.cov-map @@ -1,121 +1,167 @@ Function name: branch_if::branch_and -Raw bytes (40): 0x[01, 01, 03, 06, 0d, 05, 09, 11, 03, 06, 01, 2b, 01, 01, 10, 05, 03, 08, 00, 09, 09, 00, 0d, 00, 0e, 11, 00, 0f, 02, 06, 03, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Raw bytes (56): 0x[01, 01, 04, 05, 09, 0d, 02, 11, 0f, 0d, 02, 08, 01, 2b, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 00, 0d, 00, 0e, 20, 11, 0d, 00, 0d, 00, 0e, 11, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 3 -- expression 0 operands: lhs = Expression(1, Sub), rhs = Counter(3) -- expression 1 operands: lhs = Counter(1), rhs = Counter(2) -- expression 2 operands: lhs = Counter(4), rhs = Expression(0, Add) -Number of file 0 mappings: 6 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(3), rhs = Expression(0, Sub) +- expression 2 operands: lhs = Counter(4), rhs = Expression(3, Add) +- expression 3 operands: lhs = Counter(3), rhs = Expression(0, Sub) +Number of file 0 mappings: 8 - Code(Counter(0)) at (prev + 43, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) +- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 8) to (start + 0, 9) + true = c2 + false = (c1 - c2) - Code(Counter(2)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(4), false: Counter(3) } at (prev + 0, 13) to (start + 0, 14) + true = c4 + false = c3 - Code(Counter(4)) at (prev + 0, 15) to (start + 2, 6) -- Code(Expression(0, Add)) at (prev + 2, 12) to (start + 2, 6) - = ((c1 - c2) + c3) +- Code(Expression(3, Add)) at (prev + 2, 12) to (start + 2, 6) + = (c3 + (c1 - c2)) - Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2) - = (c4 + ((c1 - c2) + c3)) + = (c4 + (c3 + (c1 - c2))) Function name: branch_if::branch_not -Raw bytes (132): 0x[01, 01, 1d, 05, 09, 09, 02, 73, 0d, 09, 02, 0d, 6e, 73, 0d, 09, 02, 6b, 11, 0d, 6e, 73, 0d, 09, 02, 11, 66, 6b, 11, 0d, 6e, 73, 0d, 09, 02, 63, 15, 11, 66, 6b, 11, 0d, 6e, 73, 0d, 09, 02, 15, 5e, 63, 15, 11, 66, 6b, 11, 0d, 6e, 73, 0d, 09, 02, 0e, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, 73, 01, 08, 00, 0a, 6e, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 6b, 01, 08, 00, 0b, 11, 00, 0c, 02, 06, 66, 02, 06, 00, 07, 63, 01, 08, 00, 0c, 5e, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 5b, 01, 01, 00, 02] +Raw bytes (224): 0x[01, 01, 29, 05, 09, 09, 02, a3, 01, 0d, 09, 02, a3, 01, 0d, 09, 02, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 15, 8e, 01, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 12, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, a3, 01, 01, 08, 00, 0a, 20, 9e, 01, 0d, 00, 08, 00, 0a, 9e, 01, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 9b, 01, 01, 08, 00, 0b, 20, 11, 96, 01, 00, 08, 00, 0b, 11, 00, 0c, 02, 06, 96, 01, 02, 06, 00, 07, 93, 01, 01, 08, 00, 0c, 20, 8e, 01, 15, 00, 08, 00, 0c, 8e, 01, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 8b, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 29 +Number of expressions: 41 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) - expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 2 operands: lhs = Expression(28, Add), rhs = Counter(3) +- expression 2 operands: lhs = Expression(40, Add), rhs = Counter(3) - expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 4 operands: lhs = Counter(3), rhs = Expression(27, Sub) -- expression 5 operands: lhs = Expression(28, Add), rhs = Counter(3) -- expression 6 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 7 operands: lhs = Expression(26, Add), rhs = Counter(4) -- expression 8 operands: lhs = Counter(3), rhs = Expression(27, Sub) -- expression 9 operands: lhs = Expression(28, Add), rhs = Counter(3) -- expression 10 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 11 operands: lhs = Counter(4), rhs = Expression(25, Sub) -- expression 12 operands: lhs = Expression(26, Add), rhs = Counter(4) -- expression 13 operands: lhs = Counter(3), rhs = Expression(27, Sub) -- expression 14 operands: lhs = Expression(28, Add), rhs = Counter(3) -- expression 15 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 16 operands: lhs = Expression(24, Add), rhs = Counter(5) -- expression 17 operands: lhs = Counter(4), rhs = Expression(25, Sub) -- expression 18 operands: lhs = Expression(26, Add), rhs = Counter(4) -- expression 19 operands: lhs = Counter(3), rhs = Expression(27, Sub) -- expression 20 operands: lhs = Expression(28, Add), rhs = Counter(3) +- expression 4 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 5 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 6 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 7 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 8 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 9 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 10 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 11 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 12 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 13 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 14 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 15 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 16 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 17 operands: lhs = Counter(4), rhs = Expression(37, Sub) +- expression 18 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 19 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 20 operands: lhs = Expression(40, Add), rhs = Counter(3) - expression 21 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 22 operands: lhs = Counter(5), rhs = Expression(23, Sub) -- expression 23 operands: lhs = Expression(24, Add), rhs = Counter(5) -- expression 24 operands: lhs = Counter(4), rhs = Expression(25, Sub) -- expression 25 operands: lhs = Expression(26, Add), rhs = Counter(4) -- expression 26 operands: lhs = Counter(3), rhs = Expression(27, Sub) -- expression 27 operands: lhs = Expression(28, Add), rhs = Counter(3) -- expression 28 operands: lhs = Counter(2), rhs = Expression(0, Sub) -Number of file 0 mappings: 14 +- expression 22 operands: lhs = Expression(36, Add), rhs = Counter(5) +- expression 23 operands: lhs = Counter(4), rhs = Expression(37, Sub) +- expression 24 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 25 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 26 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 27 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 28 operands: lhs = Expression(36, Add), rhs = Counter(5) +- expression 29 operands: lhs = Counter(4), rhs = Expression(37, Sub) +- expression 30 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 31 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 32 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 33 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 34 operands: lhs = Counter(5), rhs = Expression(35, Sub) +- expression 35 operands: lhs = Expression(36, Add), rhs = Counter(5) +- expression 36 operands: lhs = Counter(4), rhs = Expression(37, Sub) +- expression 37 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 38 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 40 operands: lhs = Counter(2), rhs = Expression(0, Sub) +Number of file 0 mappings: 18 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) +- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 8) to (start + 0, 9) + true = c2 + false = (c1 - c2) - Code(Counter(2)) at (prev + 1, 9) to (start + 0, 17) - Code(Expression(0, Sub)) at (prev + 1, 6) to (start + 0, 7) = (c1 - c2) -- Code(Expression(28, Add)) at (prev + 1, 8) to (start + 0, 10) +- Code(Expression(40, Add)) at (prev + 1, 8) to (start + 0, 10) = (c2 + (c1 - c2)) -- Code(Expression(27, Sub)) at (prev + 0, 11) to (start + 2, 6) +- Branch { true: Expression(39, Sub), false: Counter(3) } at (prev + 0, 8) to (start + 0, 10) + true = ((c2 + (c1 - c2)) - c3) + false = c3 +- Code(Expression(39, Sub)) at (prev + 0, 11) to (start + 2, 6) = ((c2 + (c1 - c2)) - c3) - Code(Counter(3)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(26, Add)) at (prev + 1, 8) to (start + 0, 11) +- Code(Expression(38, Add)) at (prev + 1, 8) to (start + 0, 11) = (c3 + ((c2 + (c1 - c2)) - c3)) +- Branch { true: Counter(4), false: Expression(37, Sub) } at (prev + 0, 8) to (start + 0, 11) + true = c4 + false = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) - Code(Counter(4)) at (prev + 0, 12) to (start + 2, 6) -- Code(Expression(25, Sub)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(37, Sub)) at (prev + 2, 6) to (start + 0, 7) = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) -- Code(Expression(24, Add)) at (prev + 1, 8) to (start + 0, 12) +- Code(Expression(36, Add)) at (prev + 1, 8) to (start + 0, 12) = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) -- Code(Expression(23, Sub)) at (prev + 0, 13) to (start + 2, 6) +- Branch { true: Expression(35, Sub), false: Counter(5) } at (prev + 0, 8) to (start + 0, 12) + true = ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5) + false = c5 +- Code(Expression(35, Sub)) at (prev + 0, 13) to (start + 2, 6) = ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5) - Code(Counter(5)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(22, Add)) at (prev + 1, 1) to (start + 0, 2) +- Code(Expression(34, Add)) at (prev + 1, 1) to (start + 0, 2) = (c5 + ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5)) Function name: branch_if::branch_not_as -Raw bytes (91): 0x[01, 01, 10, 05, 09, 09, 02, 3f, 0d, 09, 02, 0d, 3a, 3f, 0d, 09, 02, 37, 11, 0d, 3a, 3f, 0d, 09, 02, 11, 32, 37, 11, 0d, 3a, 3f, 0d, 09, 02, 0b, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 06, 00, 07, 3f, 01, 08, 00, 15, 0d, 00, 16, 02, 06, 3a, 02, 06, 00, 07, 37, 01, 08, 00, 16, 32, 00, 17, 02, 06, 11, 02, 06, 00, 07, 2f, 01, 01, 00, 02] +Raw bytes (124): 0x[01, 01, 16, 05, 09, 09, 02, 57, 0d, 09, 02, 57, 0d, 09, 02, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 11, 4a, 4f, 11, 0d, 52, 57, 0d, 09, 02, 0e, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 20, 02, 09, 00, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 06, 00, 07, 57, 01, 08, 00, 15, 20, 0d, 52, 00, 08, 00, 15, 0d, 00, 16, 02, 06, 52, 02, 06, 00, 07, 4f, 01, 08, 00, 16, 20, 4a, 11, 00, 08, 00, 16, 4a, 00, 17, 02, 06, 11, 02, 06, 00, 07, 47, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 16 +Number of expressions: 22 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) - expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 2 operands: lhs = Expression(15, Add), rhs = Counter(3) +- expression 2 operands: lhs = Expression(21, Add), rhs = Counter(3) - expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 4 operands: lhs = Counter(3), rhs = Expression(14, Sub) -- expression 5 operands: lhs = Expression(15, Add), rhs = Counter(3) -- expression 6 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 7 operands: lhs = Expression(13, Add), rhs = Counter(4) -- expression 8 operands: lhs = Counter(3), rhs = Expression(14, Sub) -- expression 9 operands: lhs = Expression(15, Add), rhs = Counter(3) -- expression 10 operands: lhs = Counter(2), rhs = Expression(0, Sub) -- expression 11 operands: lhs = Counter(4), rhs = Expression(12, Sub) -- expression 12 operands: lhs = Expression(13, Add), rhs = Counter(4) -- expression 13 operands: lhs = Counter(3), rhs = Expression(14, Sub) -- expression 14 operands: lhs = Expression(15, Add), rhs = Counter(3) -- expression 15 operands: lhs = Counter(2), rhs = Expression(0, Sub) -Number of file 0 mappings: 11 +- expression 4 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 5 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 6 operands: lhs = Counter(3), rhs = Expression(20, Sub) +- expression 7 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 8 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 9 operands: lhs = Expression(19, Add), rhs = Counter(4) +- expression 10 operands: lhs = Counter(3), rhs = Expression(20, Sub) +- expression 11 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 12 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 13 operands: lhs = Expression(19, Add), rhs = Counter(4) +- expression 14 operands: lhs = Counter(3), rhs = Expression(20, Sub) +- expression 15 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 16 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 17 operands: lhs = Counter(4), rhs = Expression(18, Sub) +- expression 18 operands: lhs = Expression(19, Add), rhs = Counter(4) +- expression 19 operands: lhs = Counter(3), rhs = Expression(20, Sub) +- expression 20 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 21 operands: lhs = Counter(2), rhs = Expression(0, Sub) +Number of file 0 mappings: 14 - Code(Counter(0)) at (prev + 29, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 8) to (start + 0, 20) +- Branch { true: Expression(0, Sub), false: Counter(2) } at (prev + 0, 8) to (start + 0, 20) + true = (c1 - c2) + false = c2 - Code(Expression(0, Sub)) at (prev + 0, 21) to (start + 2, 6) = (c1 - c2) - Code(Counter(2)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(15, Add)) at (prev + 1, 8) to (start + 0, 21) +- Code(Expression(21, Add)) at (prev + 1, 8) to (start + 0, 21) = (c2 + (c1 - c2)) +- Branch { true: Counter(3), false: Expression(20, Sub) } at (prev + 0, 8) to (start + 0, 21) + true = c3 + false = ((c2 + (c1 - c2)) - c3) - Code(Counter(3)) at (prev + 0, 22) to (start + 2, 6) -- Code(Expression(14, Sub)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(20, Sub)) at (prev + 2, 6) to (start + 0, 7) = ((c2 + (c1 - c2)) - c3) -- Code(Expression(13, Add)) at (prev + 1, 8) to (start + 0, 22) +- Code(Expression(19, Add)) at (prev + 1, 8) to (start + 0, 22) = (c3 + ((c2 + (c1 - c2)) - c3)) -- Code(Expression(12, Sub)) at (prev + 0, 23) to (start + 2, 6) +- Branch { true: Expression(18, Sub), false: Counter(4) } at (prev + 0, 8) to (start + 0, 22) + true = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) + false = c4 +- Code(Expression(18, Sub)) at (prev + 0, 23) to (start + 2, 6) = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) - Code(Counter(4)) at (prev + 2, 6) to (start + 0, 7) -- Code(Expression(11, Add)) at (prev + 1, 1) to (start + 0, 2) +- Code(Expression(17, Add)) at (prev + 1, 1) to (start + 0, 2) = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) Function name: branch_if::branch_or -Raw bytes (42): 0x[01, 01, 04, 05, 09, 09, 0d, 0f, 11, 09, 0d, 06, 01, 35, 01, 01, 10, 05, 03, 08, 00, 09, 02, 00, 0d, 00, 0e, 0f, 00, 0f, 02, 06, 11, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Raw bytes (56): 0x[01, 01, 04, 05, 09, 09, 0d, 0f, 11, 09, 0d, 08, 01, 35, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 20, 0d, 11, 00, 0d, 00, 0e, 0f, 00, 0f, 02, 06, 11, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -123,11 +169,17 @@ Number of expressions: 4 - expression 1 operands: lhs = Counter(2), rhs = Counter(3) - expression 2 operands: lhs = Expression(3, Add), rhs = Counter(4) - expression 3 operands: lhs = Counter(2), rhs = Counter(3) -Number of file 0 mappings: 6 +Number of file 0 mappings: 8 - Code(Counter(0)) at (prev + 53, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) +- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 8) to (start + 0, 9) + true = c2 + false = (c1 - c2) - Code(Expression(0, Sub)) at (prev + 0, 13) to (start + 0, 14) = (c1 - c2) +- Branch { true: Counter(3), false: Counter(4) } at (prev + 0, 13) to (start + 0, 14) + true = c3 + false = c4 - Code(Expression(3, Add)) at (prev + 0, 15) to (start + 2, 6) = (c2 + c3) - Code(Counter(4)) at (prev + 2, 12) to (start + 2, 6) diff --git a/tests/coverage/branch_if.coverage b/tests/coverage/branch_if.coverage index babefb51d3f21..2a9a408b16aff 100644 --- a/tests/coverage/branch_if.coverage +++ b/tests/coverage/branch_if.coverage @@ -1,6 +1,6 @@ LL| |#![feature(coverage_attribute)] LL| |//@ edition: 2021 - LL| | + LL| |//@ compile-flags: -Zcoverage-options=branch LL| |//@ llvm-cov-flags: --show-branches=count LL| | LL| |macro_rules! no_merge { @@ -13,16 +13,28 @@ LL| 3| no_merge!(); LL| | LL| 3| if a { + ------------------ + | Branch (LL:8): [True: 2, False: 1] + ------------------ LL| 2| say("a") LL| 1| } LL| 3| if !a { + ------------------ + | Branch (LL:8): [True: 1, False: 2] + ------------------ LL| 1| say("not a"); LL| 2| } LL| 3| if !!a { + ------------------ + | Branch (LL:8): [True: 2, False: 1] + ------------------ LL| 2| say("not not a"); LL| 2| } ^1 LL| 3| if !!!a { + ------------------ + | Branch (LL:8): [True: 1, False: 2] + ------------------ LL| 1| say("not not not a"); LL| 2| } LL| 3|} @@ -31,13 +43,22 @@ LL| 3| no_merge!(); LL| | LL| 3| if !(a as bool) { + ------------------ + | Branch (LL:8): [True: 1, False: 2] + ------------------ LL| 1| say("not (a as bool)"); LL| 2| } LL| 3| if !!(a as bool) { + ------------------ + | Branch (LL:8): [True: 2, False: 1] + ------------------ LL| 2| say("not not (a as bool)"); LL| 2| } ^1 LL| 3| if !!!(a as bool) { + ------------------ + | Branch (LL:8): [True: 1, False: 2] + ------------------ LL| 1| say("not not (a as bool)"); LL| 2| } LL| 3|} @@ -47,6 +68,10 @@ LL| | LL| 15| if a && b { ^12 + ------------------ + | Branch (LL:8): [True: 12, False: 3] + | Branch (LL:13): [True: 8, False: 4] + ------------------ LL| 8| say("both"); LL| 8| } else { LL| 7| say("not both"); @@ -58,6 +83,10 @@ LL| | LL| 15| if a || b { ^3 + ------------------ + | Branch (LL:8): [True: 12, False: 3] + | Branch (LL:13): [True: 2, False: 1] + ------------------ LL| 14| say("either"); LL| 14| } else { LL| 1| say("neither"); diff --git a/tests/coverage/branch_if.rs b/tests/coverage/branch_if.rs index 55ef159ebdfc5..151eede75bbc7 100644 --- a/tests/coverage/branch_if.rs +++ b/tests/coverage/branch_if.rs @@ -1,6 +1,6 @@ #![feature(coverage_attribute)] //@ edition: 2021 - +//@ compile-flags: -Zcoverage-options=branch //@ llvm-cov-flags: --show-branches=count macro_rules! no_merge { diff --git a/tests/coverage/branch_while.cov-map b/tests/coverage/branch_while.cov-map index 63a7c438163ea..d5f54f1abea20 100644 --- a/tests/coverage/branch_while.cov-map +++ b/tests/coverage/branch_while.cov-map @@ -1,74 +1,98 @@ Function name: branch_while::while_cond -Raw bytes (33): 0x[01, 01, 02, 05, 09, 03, 09, 05, 01, 0c, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 10, 09, 00, 11, 02, 06, 06, 03, 01, 00, 02] +Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 0c, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 10, 20, 09, 0a, 00, 0b, 00, 10, 09, 00, 11, 02, 06, 0a, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 3 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) - expression 1 operands: lhs = Expression(0, Add), rhs = Counter(2) -Number of file 0 mappings: 5 +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 9) to (start + 0, 18) - Code(Expression(0, Add)) at (prev + 1, 11) to (start + 0, 16) = (c1 + c2) +- Branch { true: Counter(2), false: Expression(2, Sub) } at (prev + 0, 11) to (start + 0, 16) + true = c2 + false = ((c1 + c2) - c2) - Code(Counter(2)) at (prev + 0, 17) to (start + 2, 6) -- Code(Expression(1, Sub)) at (prev + 3, 1) to (start + 0, 2) +- Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2) = ((c1 + c2) - c2) Function name: branch_while::while_cond_not -Raw bytes (33): 0x[01, 01, 02, 05, 09, 03, 09, 05, 01, 15, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 14, 09, 00, 15, 02, 06, 06, 03, 01, 00, 02] +Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 15, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 14, 20, 09, 0a, 00, 0b, 00, 14, 09, 00, 15, 02, 06, 0a, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 2 +Number of expressions: 3 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) - expression 1 operands: lhs = Expression(0, Add), rhs = Counter(2) -Number of file 0 mappings: 5 +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 21, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 9) to (start + 0, 18) - Code(Expression(0, Add)) at (prev + 1, 11) to (start + 0, 20) = (c1 + c2) +- Branch { true: Counter(2), false: Expression(2, Sub) } at (prev + 0, 11) to (start + 0, 20) + true = c2 + false = ((c1 + c2) - c2) - Code(Counter(2)) at (prev + 0, 21) to (start + 2, 6) -- Code(Expression(1, Sub)) at (prev + 3, 1) to (start + 0, 2) +- Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2) = ((c1 + c2) - c2) Function name: branch_while::while_op_and -Raw bytes (40): 0x[01, 01, 03, 05, 09, 03, 0d, 11, 0d, 06, 01, 1e, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 06, 00, 14, 00, 19, 09, 00, 1a, 03, 06, 0b, 04, 01, 00, 02] +Raw bytes (56): 0x[01, 01, 04, 05, 09, 03, 0d, 03, 0d, 11, 0d, 08, 01, 1e, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 20, 0a, 0d, 00, 0b, 00, 10, 0a, 00, 14, 00, 19, 20, 09, 11, 00, 14, 00, 19, 09, 00, 1a, 03, 06, 0f, 04, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 3 +Number of expressions: 4 - expression 0 operands: lhs = Counter(1), rhs = Counter(2) - expression 1 operands: lhs = Expression(0, Add), rhs = Counter(3) -- expression 2 operands: lhs = Counter(4), rhs = Counter(3) -Number of file 0 mappings: 6 +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 3 operands: lhs = Counter(4), rhs = Counter(3) +Number of file 0 mappings: 8 - Code(Counter(0)) at (prev + 30, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 9) to (start + 1, 18) - Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 16) = (c1 + c2) -- Code(Expression(1, Sub)) at (prev + 0, 20) to (start + 0, 25) +- Branch { true: Expression(2, Sub), false: Counter(3) } at (prev + 0, 11) to (start + 0, 16) + true = ((c1 + c2) - c3) + false = c3 +- Code(Expression(2, Sub)) at (prev + 0, 20) to (start + 0, 25) = ((c1 + c2) - c3) +- Branch { true: Counter(2), false: Counter(4) } at (prev + 0, 20) to (start + 0, 25) + true = c2 + false = c4 - Code(Counter(2)) at (prev + 0, 26) to (start + 3, 6) -- Code(Expression(2, Add)) at (prev + 4, 1) to (start + 0, 2) +- Code(Expression(3, Add)) at (prev + 4, 1) to (start + 0, 2) = (c4 + c3) Function name: branch_while::while_op_or -Raw bytes (46): 0x[01, 01, 06, 05, 0f, 09, 0d, 03, 09, 09, 0d, 16, 0d, 03, 09, 06, 01, 29, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 16, 00, 14, 00, 19, 0f, 00, 1a, 03, 06, 12, 04, 01, 00, 02] +Raw bytes (66): 0x[01, 01, 09, 05, 1b, 09, 0d, 03, 09, 03, 09, 22, 0d, 03, 09, 09, 0d, 22, 0d, 03, 09, 08, 01, 29, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 20, 09, 22, 00, 0b, 00, 10, 22, 00, 14, 00, 19, 20, 0d, 1e, 00, 14, 00, 19, 1b, 00, 1a, 03, 06, 1e, 04, 01, 00, 02] Number of files: 1 - file 0 => global file 1 -Number of expressions: 6 -- expression 0 operands: lhs = Counter(1), rhs = Expression(3, Add) +Number of expressions: 9 +- expression 0 operands: lhs = Counter(1), rhs = Expression(6, Add) - expression 1 operands: lhs = Counter(2), rhs = Counter(3) - expression 2 operands: lhs = Expression(0, Add), rhs = Counter(2) -- expression 3 operands: lhs = Counter(2), rhs = Counter(3) -- expression 4 operands: lhs = Expression(5, Sub), rhs = Counter(3) +- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(2) +- expression 4 operands: lhs = Expression(8, Sub), rhs = Counter(3) - expression 5 operands: lhs = Expression(0, Add), rhs = Counter(2) -Number of file 0 mappings: 6 +- expression 6 operands: lhs = Counter(2), rhs = Counter(3) +- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(3) +- expression 8 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of file 0 mappings: 8 - Code(Counter(0)) at (prev + 41, 1) to (start + 1, 16) - Code(Counter(1)) at (prev + 3, 9) to (start + 1, 18) - Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 16) = (c1 + (c2 + c3)) -- Code(Expression(5, Sub)) at (prev + 0, 20) to (start + 0, 25) +- Branch { true: Counter(2), false: Expression(8, Sub) } at (prev + 0, 11) to (start + 0, 16) + true = c2 + false = ((c1 + (c2 + c3)) - c2) +- Code(Expression(8, Sub)) at (prev + 0, 20) to (start + 0, 25) = ((c1 + (c2 + c3)) - c2) -- Code(Expression(3, Add)) at (prev + 0, 26) to (start + 3, 6) +- Branch { true: Counter(3), false: Expression(7, Sub) } at (prev + 0, 20) to (start + 0, 25) + true = c3 + false = (((c1 + (c2 + c3)) - c2) - c3) +- Code(Expression(6, Add)) at (prev + 0, 26) to (start + 3, 6) = (c2 + c3) -- Code(Expression(4, Sub)) at (prev + 4, 1) to (start + 0, 2) +- Code(Expression(7, Sub)) at (prev + 4, 1) to (start + 0, 2) = (((c1 + (c2 + c3)) - c2) - c3) diff --git a/tests/coverage/branch_while.coverage b/tests/coverage/branch_while.coverage index d2351a7de09a2..8d9a6c3bc68b7 100644 --- a/tests/coverage/branch_while.coverage +++ b/tests/coverage/branch_while.coverage @@ -1,6 +1,6 @@ LL| |#![feature(coverage_attribute)] LL| |//@ edition: 2021 - LL| | + LL| |//@ compile-flags: -Zcoverage-options=branch LL| |//@ llvm-cov-flags: --show-branches=count LL| | LL| |macro_rules! no_merge { @@ -14,6 +14,9 @@ LL| | LL| 1| let mut a = 8; LL| 9| while a > 0 { + ------------------ + | Branch (LL:11): [True: 8, False: 1] + ------------------ LL| 8| a -= 1; LL| 8| } LL| 1|} @@ -23,6 +26,9 @@ LL| | LL| 1| let mut a = 8; LL| 9| while !(a == 0) { + ------------------ + | Branch (LL:11): [True: 8, False: 1] + ------------------ LL| 8| a -= 1; LL| 8| } LL| 1|} @@ -33,6 +39,10 @@ LL| 1| let mut a = 8; LL| 1| let mut b = 4; LL| 5| while a > 0 && b > 0 { + ------------------ + | Branch (LL:11): [True: 5, False: 0] + | Branch (LL:20): [True: 4, False: 1] + ------------------ LL| 4| a -= 1; LL| 4| b -= 1; LL| 4| } @@ -45,6 +55,10 @@ LL| 1| let mut b = 8; LL| 9| while a > 0 || b > 0 { ^5 + ------------------ + | Branch (LL:11): [True: 4, False: 5] + | Branch (LL:20): [True: 4, False: 1] + ------------------ LL| 8| a -= 1; LL| 8| b -= 1; LL| 8| } diff --git a/tests/coverage/branch_while.rs b/tests/coverage/branch_while.rs index 99e9a798eff5f..507815fbecbeb 100644 --- a/tests/coverage/branch_while.rs +++ b/tests/coverage/branch_while.rs @@ -1,6 +1,6 @@ #![feature(coverage_attribute)] //@ edition: 2021 - +//@ compile-flags: -Zcoverage-options=branch //@ llvm-cov-flags: --show-branches=count macro_rules! no_merge { From 060c7ce7e9e09c463352a1cabd3ea1d7264deef2 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 13 Mar 2024 20:53:10 +1100 Subject: [PATCH 53/79] coverage: `-Zcoverage-options=branch` is no longer a placeholder --- compiler/rustc_session/src/config.rs | 2 +- src/doc/rustc/src/instrument-coverage.md | 2 +- src/doc/unstable-book/src/compiler-flags/coverage-options.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 5c52ee66128c9..b7ee2c9802541 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -146,7 +146,7 @@ pub enum InstrumentCoverage { /// Individual flag values controlled by `-Z coverage-options`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct CoverageOptions { - /// Add branch coverage instrumentation (placeholder flag; not yet implemented). + /// Add branch coverage instrumentation. pub branch: bool, } diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 7780f2102ba6b..185a3ba5dbd41 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -352,7 +352,7 @@ This unstable option provides finer control over some aspects of coverage instrumentation. Pass one or more of the following values, separated by commas. - `branch` or `no-branch` - - Placeholder for potential branch coverage support in the future. + - Enables or disables branch coverage instrumentation. ## Other references diff --git a/src/doc/unstable-book/src/compiler-flags/coverage-options.md b/src/doc/unstable-book/src/compiler-flags/coverage-options.md index 105dce6151178..450573cc6c746 100644 --- a/src/doc/unstable-book/src/compiler-flags/coverage-options.md +++ b/src/doc/unstable-book/src/compiler-flags/coverage-options.md @@ -5,4 +5,4 @@ This option controls details of the coverage instrumentation performed by Multiple options can be passed, separated by commas. Valid options are: -- `branch` or `no-branch`: Placeholder for future branch coverage support. +- `branch` or `no-branch`: Enables or disables branch coverage instrumentation. From 5dd44f43c7bfa301d768c043c97067788de0aaff Mon Sep 17 00:00:00 2001 From: surechen Date: Thu, 14 Mar 2024 17:12:39 +0800 Subject: [PATCH 54/79] change some attribute to only_local --- compiler/rustc_feature/src/builtin_attrs.rs | 73 ++++++++++++++++----- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 38848b22cb296..1f77484d65ce0 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -894,56 +894,93 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_strict_coherence, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_variance_of_opaques, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_hidden_type_of_opaques, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing, @only_local: true), + rustc_attr!( + TEST, rustc_variance_of_opaques, Normal, template!(Word), + WarnFollowing, @only_local: true + ), + rustc_attr!( + TEST, rustc_hidden_type_of_opaques, Normal, template!(Word), + WarnFollowing, @only_local: true), rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."), WarnFollowing), - rustc_attr!(TEST, rustc_abi, Normal, template!(List: "field1, field2, ..."), WarnFollowing), - rustc_attr!(TEST, rustc_regions, Normal, template!(Word), WarnFollowing), + rustc_attr!( + TEST, rustc_abi, Normal, template!(List: "field1, field2, ..."), + WarnFollowing, @only_local: true + ), + rustc_attr!( + TEST, rustc_regions, Normal, template!(Word), + WarnFollowing, @only_local: true + ), rustc_attr!( TEST, rustc_error, Normal, template!(Word, List: "delayed_bug_from_inside_query"), WarnFollowingWordOnly ), - rustc_attr!(TEST, rustc_dump_user_args, Normal, template!(Word), WarnFollowing), + rustc_attr!( + TEST, rustc_dump_user_args, Normal, template!(Word), WarnFollowing, + @only_local: true + ), rustc_attr!(TEST, rustc_evaluate_where_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!( - TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode"), DuplicatesOk + TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode"), + DuplicatesOk, @only_local: true ), rustc_attr!( - TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode"), DuplicatesOk + TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode"), + DuplicatesOk, @only_local: true ), rustc_attr!( TEST, rustc_clean, Normal, template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#), - DuplicatesOk, + DuplicatesOk, @only_local: true ), rustc_attr!( TEST, rustc_partition_reused, Normal, - template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, + template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, @only_local: true ), rustc_attr!( TEST, rustc_partition_codegened, Normal, - template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, + template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, @only_local: true ), rustc_attr!( TEST, rustc_expected_cgu_reuse, Normal, template!(List: r#"cfg = "...", module = "...", kind = "...""#), DuplicatesOk, + @only_local: true + ), + rustc_attr!( + TEST, rustc_symbol_name, Normal, template!(Word), WarnFollowing, + @only_local: true ), - rustc_attr!(TEST, rustc_symbol_name, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing), + rustc_attr!( + TEST, rustc_def_path, Normal, template!(Word), WarnFollowing, + @only_local: true + ), rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk), gated!( custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#), - ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite", + ErrorFollowing, @only_local: true, + "the `#[custom_mir]` attribute is just used for the Rust test suite", + ), + rustc_attr!( + TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing, + @only_local: true + ), + rustc_attr!( + TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing, + @only_local: true + ), + rustc_attr!( + TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing, + @only_local: true ), - rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_dump_vtable, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), DuplicatesOk), + rustc_attr!( + TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), DuplicatesOk, + @only_local: true + ), gated!( omit_gdb_pretty_printer_section, Normal, template!(Word), WarnFollowing, + @only_local: true, "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite", ), rustc_attr!( From 25411113c1185bd08841bbedeb4e52279f8e5f13 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 14 Mar 2024 10:49:28 +0100 Subject: [PATCH 55/79] Ungate the `UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES` lint This was missed during stablisation of the `#[diagnostic]` attribute namespace. Fixes #122446 --- compiler/rustc_lint_defs/src/builtin.rs | 1 - .../deny_malformed_attribute.rs | 7 +++++++ .../deny_malformed_attribute.stderr | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/ui/diagnostic_namespace/deny_malformed_attribute.rs create mode 100644 tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 20e492dbd8a78..b9e183f48f44a 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4341,7 +4341,6 @@ declare_lint! { pub UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, Warn, "unrecognized or malformed diagnostic attribute", - @feature_gate = sym::diagnostic_namespace; } declare_lint! { diff --git a/tests/ui/diagnostic_namespace/deny_malformed_attribute.rs b/tests/ui/diagnostic_namespace/deny_malformed_attribute.rs new file mode 100644 index 0000000000000..1d946a14aff1a --- /dev/null +++ b/tests/ui/diagnostic_namespace/deny_malformed_attribute.rs @@ -0,0 +1,7 @@ +#![deny(unknown_or_malformed_diagnostic_attributes)] + +#[diagnostic::unknown_attribute] +//~^ERROR unknown diagnostic attribute +struct Foo; + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr b/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr new file mode 100644 index 0000000000000..a646d3613de78 --- /dev/null +++ b/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr @@ -0,0 +1,14 @@ +error: unknown diagnostic attribute + --> $DIR/deny_malformed_attribute.rs:3:15 + | +LL | #[diagnostic::unknown_attribute] + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deny_malformed_attribute.rs:1:9 + | +LL | #![deny(unknown_or_malformed_diagnostic_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 71f1943cbf436436a0a8d64c86055ba0c0770c94 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 13 Mar 2024 09:20:16 +0000 Subject: [PATCH 56/79] Fix accidental re-addition of removed code in a previous PR --- compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 5f4408ebbc6c2..a66a95a5f0c21 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -381,10 +381,7 @@ pub fn eval_in_interpreter<'mir, 'tcx>( Ok(mplace) => { // Since evaluation had no errors, validate the resulting constant. - // Temporarily allow access to the static_root_ids for the purpose of validation. - let static_root_ids = ecx.machine.static_root_ids.take(); let res = const_validate_mplace(&ecx, &mplace, cid); - ecx.machine.static_root_ids = static_root_ids; let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); From d6c999754c5a4d6d2a1e264825e71c56b394cbb0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 12:17:30 +0000 Subject: [PATCH 57/79] Generalize `eval_in_interpreter` with a helper trait --- .../src/const_eval/eval_queries.rs | 44 ++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index a66a95a5f0c21..1b401cc5cc0d1 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -290,10 +290,36 @@ pub fn eval_static_initializer_provider<'tcx>( // they do not have to behave "as if" they were evaluated at runtime. CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error), ); - let alloc_id = eval_in_interpreter(&mut ecx, cid, true)?.alloc_id; - let alloc = take_static_root_alloc(&mut ecx, alloc_id); - let alloc = tcx.mk_const_alloc(alloc); - Ok(alloc) + eval_in_interpreter(&mut ecx, cid, true) +} + +trait InterpretationResult<'tcx> { + /// This function takes the place where the result of the evaluation is stored + /// and prepares it for returning it in the appropriate format needed by the specific + /// evaluation query. + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self; +} + +impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self { + let alloc = take_static_root_alloc(ecx, mplace.ptr().provenance.unwrap().alloc_id()); + ecx.tcx.mk_const_alloc(alloc) + } +} + +impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + _ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self { + ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty } + } } #[instrument(skip(tcx), level = "debug")] @@ -336,11 +362,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>( eval_in_interpreter(&mut ecx, cid, is_static) } -pub fn eval_in_interpreter<'mir, 'tcx>( +fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, cid: GlobalId<'tcx>, is_static: bool, -) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { +) -> Result { // `is_static` just means "in static", it could still be a promoted! debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some()); @@ -383,14 +409,12 @@ pub fn eval_in_interpreter<'mir, 'tcx>( let res = const_validate_mplace(&ecx, &mplace, cid); - let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); - // Validation failed, report an error. if let Err(error) = res { + let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); Err(const_report_error(&ecx, error, alloc_id)) } else { - // Convert to raw constant - Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty }) + Ok(R::make_result(mplace, ecx)) } } } From 93888cd0a401581bc46f4bd85a1bf33d8ac14c7f Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 12:33:09 +0000 Subject: [PATCH 58/79] Move only usage of `take_static_root_alloc` to its definition and inline it --- .../src/const_eval/eval_queries.rs | 18 ++++------------ .../rustc_const_eval/src/interpret/intern.rs | 2 +- .../rustc_const_eval/src/interpret/mod.rs | 2 +- .../rustc_const_eval/src/interpret/util.rs | 21 ++++++++++++------- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 1b401cc5cc0d1..63b1d485a24f2 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -18,9 +18,9 @@ use crate::errors; use crate::errors::ConstEvalError; use crate::interpret::eval_nullary_intrinsic; use crate::interpret::{ - create_static_alloc, intern_const_alloc_recursive, take_static_root_alloc, CtfeValidationMode, - GlobalId, Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, - OpTy, RefTracking, StackPopCleanup, + create_static_alloc, intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, + InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, + StackPopCleanup, }; // Returns a pointer to where the result lives @@ -293,7 +293,7 @@ pub fn eval_static_initializer_provider<'tcx>( eval_in_interpreter(&mut ecx, cid, true) } -trait InterpretationResult<'tcx> { +pub trait InterpretationResult<'tcx> { /// This function takes the place where the result of the evaluation is stored /// and prepares it for returning it in the appropriate format needed by the specific /// evaluation query. @@ -303,16 +303,6 @@ trait InterpretationResult<'tcx> { ) -> Self; } -impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { - fn make_result<'mir>( - mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, - ) -> Self { - let alloc = take_static_root_alloc(ecx, mplace.ptr().provenance.unwrap().alloc_id()); - ecx.tcx.mk_const_alloc(alloc) - } -} - impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index c30a13624178b..17bb59aae8f17 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -176,7 +176,7 @@ pub fn intern_const_alloc_recursive< // This gives us the initial set of nested allocations, which will then all be processed // recursively in the loop below. let mut todo: Vec<_> = if is_static { - // Do not steal the root allocation, we need it later for `take_static_root_alloc` + // Do not steal the root allocation, we need it later to create the return value of `eval_static_initializer`. // But still change its mutability to match the requested one. let alloc = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap(); alloc.1.mutability = base_mutability; diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 2ed879ca72b5f..474d35b2aa3a2 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -39,5 +39,5 @@ use self::{ }; pub(crate) use self::intrinsics::eval_nullary_intrinsic; -pub(crate) use self::util::{create_static_alloc, take_static_root_alloc}; +pub(crate) use self::util::create_static_alloc; use eval_context::{from_known_layout, mir_assign_valid_types}; diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 086475f72c5d4..c83ef14c03fe7 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,14 +1,15 @@ -use crate::const_eval::CompileTimeEvalContext; +use crate::const_eval::{CompileTimeEvalContext, CompileTimeInterpreter, InterpretationResult}; use crate::interpret::{MemPlaceMeta, MemoryKind}; use rustc_hir::def_id::LocalDefId; -use rustc_middle::mir::interpret::{AllocId, Allocation, InterpResult, Pointer}; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; use std::ops::ControlFlow; -use super::MPlaceTy; +use super::{InterpCx, MPlaceTy}; /// Checks whether a type contains generic parameters which must be instantiated. /// @@ -80,11 +81,15 @@ where } } -pub(crate) fn take_static_root_alloc<'mir, 'tcx: 'mir>( - ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, - alloc_id: AllocId, -) -> Allocation { - ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1 +impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self { + let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); + let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1; + ecx.tcx.mk_const_alloc(alloc) + } } pub(crate) fn create_static_alloc<'mir, 'tcx: 'mir>( From 8b8efd157b075d2e3c35bd0d3adc23981f919b07 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 13:02:33 +0000 Subject: [PATCH 59/79] Move error handling into const_validate_mplace --- .../src/const_eval/eval_queries.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 63b1d485a24f2..6da8cf433c618 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -396,16 +396,9 @@ fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( } Ok(mplace) => { // Since evaluation had no errors, validate the resulting constant. + const_validate_mplace(&ecx, &mplace, cid)?; - let res = const_validate_mplace(&ecx, &mplace, cid); - - // Validation failed, report an error. - if let Err(error) = res { - let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); - Err(const_report_error(&ecx, error, alloc_id)) - } else { - Ok(R::make_result(mplace, ecx)) - } + Ok(R::make_result(mplace, ecx)) } } } @@ -415,7 +408,8 @@ pub fn const_validate_mplace<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, mplace: &MPlaceTy<'tcx>, cid: GlobalId<'tcx>, -) -> InterpResult<'tcx> { +) -> Result<(), ErrorHandled> { + let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let mut ref_tracking = RefTracking::new(mplace.clone()); let mut inner = false; while let Some((mplace, path)) = ref_tracking.todo.pop() { @@ -429,7 +423,8 @@ pub fn const_validate_mplace<'mir, 'tcx>( CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner } } }; - ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; + ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) + .map_err(|error| const_report_error(&ecx, error, alloc_id))?; inner = true; } From 6b936b6c081394a77fa8272bace9c1ce22332a1b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 13:04:05 +0000 Subject: [PATCH 60/79] Move InterpCx into eval_in_interpreter --- .../src/const_eval/eval_queries.rs | 16 ++++++++-------- compiler/rustc_const_eval/src/interpret/util.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 6da8cf433c618..3ad53731e8a79 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -282,7 +282,7 @@ pub fn eval_static_initializer_provider<'tcx>( let instance = ty::Instance::mono(tcx, def_id.to_def_id()); let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None }; - let mut ecx = InterpCx::new( + let ecx = InterpCx::new( tcx, tcx.def_span(def_id), ty::ParamEnv::reveal_all(), @@ -290,7 +290,7 @@ pub fn eval_static_initializer_provider<'tcx>( // they do not have to behave "as if" they were evaluated at runtime. CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error), ); - eval_in_interpreter(&mut ecx, cid, true) + eval_in_interpreter(ecx, cid, true) } pub trait InterpretationResult<'tcx> { @@ -299,14 +299,14 @@ pub trait InterpretationResult<'tcx> { /// evaluation query. fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self; } impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - _ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + _ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self { ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty } } @@ -339,7 +339,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( let def = cid.instance.def.def_id(); let is_static = tcx.is_static(def); - let mut ecx = InterpCx::new( + let ecx = InterpCx::new( tcx, tcx.def_span(def), key.param_env, @@ -349,11 +349,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // so we have to reject reading mutable global memory. CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); - eval_in_interpreter(&mut ecx, cid, is_static) + eval_in_interpreter(ecx, cid, is_static) } fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, cid: GlobalId<'tcx>, is_static: bool, ) -> Result { @@ -361,7 +361,7 @@ fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some()); let res = ecx.load_mir(cid.instance.def, cid.promoted); - match res.and_then(|body| eval_body_using_ecx(ecx, cid, body)) { + match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) { Err(error) => { let (error, backtrace) = error.into_parts(); backtrace.print_backtrace(); diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index c83ef14c03fe7..10b5e3ff1df5a 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -84,7 +84,7 @@ where impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self { let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1; From 69d781abef67abdc8cac65b02bbf75a940734691 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 14 Mar 2024 20:18:04 +0800 Subject: [PATCH 61/79] move impl documentation to their actual locations --- compiler/rustc_target/src/abi/mod.rs | 1 - .../src/solve/assembly/structural_traits.rs | 10 ++++++ .../src/solve/trait_goals.rs | 35 ++----------------- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index 8d1c7c77bb698..24e49ff648f2f 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -121,7 +121,6 @@ impl<'a> Layout<'a> { /// /// Currently, that means that the type is pointer-sized, pointer-aligned, /// and has a initialized (non-union), scalar ABI. - // Please also update compiler/rustc_trait_selection/src/solve/trait_goals.rs if the criteria changes pub fn is_pointer_like(self, data_layout: &TargetDataLayout) -> bool { self.size() == data_layout.pointer_size && self.align().abi == data_layout.pointer_align.abi diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index af533d8db7149..2bfb86b592b0d 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -120,6 +120,8 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( ty: Ty<'tcx>, ) -> Result>>, NoSolution> { match *ty.kind() { + // impl Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char, &mut? T, [T; N], dyn* Trait, ! + // impl Sized for Coroutine, CoroutineWitness, Closure, CoroutineClosure ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) @@ -152,8 +154,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( bug!("unexpected type `{ty}`") } + // impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()), + // impl Sized for Adt where T: Sized forall T in field types ty::Adt(def, args) => { let sized_crit = def.sized_constraint(ecx.tcx()); Ok(sized_crit.iter_instantiated(ecx.tcx(), args).map(ty::Binder::dummy).collect()) @@ -167,6 +171,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( ty: Ty<'tcx>, ) -> Result>>, NoSolution> { match *ty.kind() { + // impl Copy/Clone for FnDef, FnPtr ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]), // Implementations are provided in core @@ -196,12 +201,16 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( bug!("unexpected type `{ty}`") } + // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()), + // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]), ty::CoroutineClosure(..) => Err(NoSolution), + // only when `coroutine_clone` is enabled and the coroutine is movable + // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) ty::Coroutine(def_id, args) => match ecx.tcx().coroutine_movability(def_id) { Movability::Static => Err(NoSolution), Movability::Movable => { @@ -217,6 +226,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( } }, + // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types ty::CoroutineWitness(def_id, args) => Ok(ecx .tcx() .coroutine_hidden_types(def_id) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 9c2a02a571786..ff3fd84af8b06 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -188,16 +188,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }) } - /// ```rust,ignore (not valid rust syntax) - /// impl Sized for u*, i*, bool, f*, FnPtr, FnDef, *(const/mut) T, char, &mut? T, [T; N], dyn* Trait, ! - /// - /// impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized - /// - /// impl Sized for Adt where T: Sized forall T in field types - /// ``` - /// - /// note that `[T; N]` is unconditionally sized since `T: Sized` is required for the array type to be - /// well-formed. fn consider_builtin_sized_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -212,20 +202,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } - /// ```rust,ignore (not valid rust syntax) - /// impl Copy/Clone for FnDef, FnPtr - /// - /// impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone - /// - /// impl Copy/Clone for Closure where T: Copy/Clone forall T in upvars - /// - /// // only when `coroutine_clone` is enabled and the coroutine is movable - /// impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) - /// - /// impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types - /// ``` - /// - /// Some built-in types don't have built-in impls because they can be implemented within the standard library. fn consider_builtin_copy_clone_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -240,9 +216,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ) } - /// Implements `PointerLike` for types that are pointer-sized, pointer-aligned, - /// and have a initialized (non-union), scalar ABI. - // Please also update compiler/rustc_target/src/abi/mod.rs if the criteria changes fn consider_builtin_pointer_like_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, @@ -272,18 +245,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } } - /// ```rust,ignore (not valid rust syntax) - /// impl FnPtr for FnPtr {} - /// impl !FnPtr for T where T != FnPtr && T is rigid {} - /// ``` - /// - /// Note: see [`Ty::is_known_rigid`] for what it means for the type to be rigid. fn consider_builtin_fn_ptr_trait_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let self_ty = goal.predicate.self_ty(); match goal.predicate.polarity { + // impl FnPtr for FnPtr {} ty::ImplPolarity::Positive => { if self_ty.is_fn_ptr() { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -291,6 +259,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { Err(NoSolution) } } + // impl !FnPtr for T where T != FnPtr && T is rigid {} ty::ImplPolarity::Negative => { // If a type is rigid and not a fn ptr, then we know for certain // that it does *not* implement `FnPtr`. From d2d2bd273686717aa0359d994cf2333738af7071 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 13:20:12 +0000 Subject: [PATCH 62/79] Move generate_stacktrace_from_stack away from InterpCx to avoid having to know the `Machine` type --- .../rustc_const_eval/src/const_eval/error.rs | 9 +-- .../rustc_const_eval/src/const_eval/mod.rs | 2 +- .../src/interpret/eval_context.rs | 56 +++++++++---------- src/tools/miri/src/diagnostics.rs | 2 +- 4 files changed, 32 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index b6adee435ba3a..c74f39dd1635a 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -8,9 +8,9 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{layout::LayoutError, ConstInt}; use rustc_span::{Span, Symbol, DUMMY_SP}; -use super::{CompileTimeInterpreter, InterpCx}; +use super::CompileTimeInterpreter; use crate::errors::{self, FrameNote, ReportErrorExt}; -use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopType}; +use crate::interpret::{ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType}; /// The CTFE machine has some custom error kinds. #[derive(Clone, Debug)] @@ -63,10 +63,7 @@ pub fn get_span_and_frames<'tcx, 'mir>( where 'tcx: 'mir, { - let mut stacktrace = - InterpCx::>::generate_stacktrace_from_stack( - &machine.stack, - ); + let mut stacktrace = Frame::generate_stacktrace_from_stack(&machine.stack); // Filter out `requires_caller_location` frames. stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); let span = stacktrace.first().map(|f| f.span).unwrap_or(tcx.span); diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 289dcb7d01d68..d0d6adbfad069 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::interpret::InterpErrorInfo; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, Ty}; -use crate::interpret::{format_interp_error, InterpCx}; +use crate::interpret::format_interp_error; mod error; mod eval_queries; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 7526acf145436..09e9cc4b35d31 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -283,6 +283,32 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> { pub(super) fn locals_addr(&self) -> usize { self.locals.raw.as_ptr().addr() } + + #[must_use] + pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec> { + let mut frames = Vec::new(); + // This deliberately does *not* honor `requires_caller_location` since it is used for much + // more than just panics. + for frame in stack.iter().rev() { + let span = match frame.loc { + Left(loc) => { + // If the stacktrace passes through MIR-inlined source scopes, add them. + let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); + let mut scope_data = &frame.body.source_scopes[scope]; + while let Some((instance, call_span)) = scope_data.inlined { + frames.push(FrameInfo { span, instance }); + span = call_span; + scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; + } + span + } + Right(span) => span, + }; + frames.push(FrameInfo { span, instance: frame.instance }); + } + trace!("generate stacktrace: {:#?}", frames); + frames + } } // FIXME: only used by miri, should be removed once translatable. @@ -1170,37 +1196,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { PlacePrinter { ecx: self, place: *place.place() } } - #[must_use] - pub fn generate_stacktrace_from_stack( - stack: &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>], - ) -> Vec> { - let mut frames = Vec::new(); - // This deliberately does *not* honor `requires_caller_location` since it is used for much - // more than just panics. - for frame in stack.iter().rev() { - let span = match frame.loc { - Left(loc) => { - // If the stacktrace passes through MIR-inlined source scopes, add them. - let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); - let mut scope_data = &frame.body.source_scopes[scope]; - while let Some((instance, call_span)) = scope_data.inlined { - frames.push(FrameInfo { span, instance }); - span = call_span; - scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; - } - span - } - Right(span) => span, - }; - frames.push(FrameInfo { span, instance: frame.instance }); - } - trace!("generate stacktrace: {:#?}", frames); - frames - } - #[must_use] pub fn generate_stacktrace(&self) -> Vec> { - Self::generate_stacktrace_from_stack(self.stack()) + Frame::generate_stacktrace_from_stack(self.stack()) } } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 4683965159d73..6e612ea34a70f 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -528,7 +528,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { use NonHaltingDiagnostic::*; let stacktrace = - MiriInterpCx::generate_stacktrace_from_stack(self.threads.active_thread_stack()); + Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack()); let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self); let (title, diag_level) = match &e { From d3b7b558aa876e563d35090ae02b0b61430818de Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 11 Mar 2024 13:21:42 +0000 Subject: [PATCH 63/79] Directly pass in the stack instead of computing it from a machine --- compiler/rustc_const_eval/src/const_eval/error.rs | 7 ++++--- compiler/rustc_const_eval/src/const_eval/eval_queries.rs | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index c74f39dd1635a..763344207c467 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -2,6 +2,7 @@ use std::mem; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg}; use rustc_hir::CRATE_HIR_ID; +use rustc_middle::mir::interpret::Provenance; use rustc_middle::mir::AssertKind; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::TyCtxt; @@ -58,12 +59,12 @@ impl<'tcx> Into> for ConstEvalErrKind { pub fn get_span_and_frames<'tcx, 'mir>( tcx: TyCtxtAt<'tcx>, - machine: &CompileTimeInterpreter<'mir, 'tcx>, + stack: &[Frame<'mir, 'tcx, impl Provenance, impl Sized>], ) -> (Span, Vec) where 'tcx: 'mir, { - let mut stacktrace = Frame::generate_stacktrace_from_stack(&machine.stack); + let mut stacktrace = Frame::generate_stacktrace_from_stack(stack); // Filter out `requires_caller_location` frames. stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); let span = stacktrace.first().map(|f| f.span).unwrap_or(tcx.span); @@ -167,7 +168,7 @@ pub(super) fn lint<'tcx, 'mir, L>( ) where L: for<'a> rustc_errors::LintDiagnostic<'a, ()>, { - let (span, frames) = get_span_and_frames(tcx, machine); + let (span, frames) = get_span_and_frames(tcx, &machine.stack); tcx.emit_node_span_lint( lint, diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 3ad53731e8a79..439cb5b03b943 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -385,7 +385,7 @@ fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( *ecx.tcx, error, None, - || super::get_span_and_frames(ecx.tcx, &ecx.machine), + || super::get_span_and_frames(ecx.tcx, ecx.stack()), |span, frames| ConstEvalError { span, error_kind: kind, @@ -450,7 +450,7 @@ pub fn const_report_error<'mir, 'tcx>( *ecx.tcx, error, None, - || crate::const_eval::get_span_and_frames(ecx.tcx, &ecx.machine), + || crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()), move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes }, ) } From 02a0ac805823fa696b2d5b3b8c6da3324190d21a Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Mar 2024 09:34:57 +0000 Subject: [PATCH 64/79] Remove an argument that can be computed cheaply --- .../rustc_const_eval/src/const_eval/eval_queries.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 439cb5b03b943..7d0ce9930d5c7 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -290,7 +290,7 @@ pub fn eval_static_initializer_provider<'tcx>( // they do not have to behave "as if" they were evaluated at runtime. CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error), ); - eval_in_interpreter(ecx, cid, true) + eval_in_interpreter(ecx, cid) } pub trait InterpretationResult<'tcx> { @@ -349,24 +349,20 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // so we have to reject reading mutable global memory. CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); - eval_in_interpreter(ecx, cid, is_static) + eval_in_interpreter(ecx, cid) } fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, cid: GlobalId<'tcx>, - is_static: bool, ) -> Result { - // `is_static` just means "in static", it could still be a promoted! - debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some()); - let res = ecx.load_mir(cid.instance.def, cid.promoted); match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) { Err(error) => { let (error, backtrace) = error.into_parts(); backtrace.print_backtrace(); - let (kind, instance) = if is_static { + let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { ("static", String::new()) } else { // If the current item has generics, we'd like to enrich the message with the From cc7e0b22003defc5f99fe8048e09ad4f730768c4 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Mar 2024 09:41:49 +0000 Subject: [PATCH 65/79] Share the `InterpCx` creation between static and const evaluation --- .../src/const_eval/eval_queries.rs | 30 +++++++------------ 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 7d0ce9930d5c7..2608107826fcd 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -282,15 +282,7 @@ pub fn eval_static_initializer_provider<'tcx>( let instance = ty::Instance::mono(tcx, def_id.to_def_id()); let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None }; - let ecx = InterpCx::new( - tcx, - tcx.def_span(def_id), - ty::ParamEnv::reveal_all(), - // Statics (and promoteds inside statics) may access other statics, because unlike consts - // they do not have to behave "as if" they were evaluated at runtime. - CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error), - ); - eval_in_interpreter(ecx, cid) + eval_in_interpreter(tcx, cid, ty::ParamEnv::reveal_all()) } pub trait InterpretationResult<'tcx> { @@ -335,27 +327,27 @@ pub fn eval_to_allocation_raw_provider<'tcx>( trace!("const eval: {:?} ({})", key, instance); } - let cid = key.value; + eval_in_interpreter(tcx, key.value, key.param_env) +} + +fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( + tcx: TyCtxt<'tcx>, + cid: GlobalId<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Result { let def = cid.instance.def.def_id(); let is_static = tcx.is_static(def); - let ecx = InterpCx::new( + let mut ecx = InterpCx::new( tcx, tcx.def_span(def), - key.param_env, + param_env, // Statics (and promoteds inside statics) may access mutable global memory, because unlike consts // they do not have to behave "as if" they were evaluated at runtime. // For consts however we want to ensure they behave "as if" they were evaluated at runtime, // so we have to reject reading mutable global memory. CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); - eval_in_interpreter(ecx, cid) -} - -fn eval_in_interpreter<'mir, 'tcx, R: InterpretationResult<'tcx>>( - mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, - cid: GlobalId<'tcx>, -) -> Result { let res = ecx.load_mir(cid.instance.def, cid.promoted); match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) { Err(error) => { From 2e6c4900b63c401cea4e4b0492e075def65508dd Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Mar 2024 10:04:36 +0000 Subject: [PATCH 66/79] Move validation into eval_body_using_ecx --- .../rustc_const_eval/src/const_eval/eval_queries.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 2608107826fcd..4f41c977f2e20 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -84,6 +84,9 @@ fn eval_body_using_ecx<'mir, 'tcx>( // Intern the result intern_const_alloc_recursive(ecx, intern_kind, &ret)?; + // Since evaluation had no errors, validate the resulting constant. + const_validate_mplace(&ecx, &ret, cid)?; + Ok(ret) } @@ -382,12 +385,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( }, )) } - Ok(mplace) => { - // Since evaluation had no errors, validate the resulting constant. - const_validate_mplace(&ecx, &mplace, cid)?; - - Ok(R::make_result(mplace, ecx)) - } + Ok(mplace) => Ok(R::make_result(mplace, ecx)), } } From 16046c77aad72d7be16253ec029b568b157d82d4 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 12 Mar 2024 13:41:41 +0000 Subject: [PATCH 67/79] Move the entire success path into `eval_body_using_ecx` --- .../src/const_eval/eval_queries.rs | 72 +++++++++---------- .../rustc_const_eval/src/interpret/util.rs | 2 +- 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 4f41c977f2e20..d62ab39d0ecfc 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -24,12 +24,12 @@ use crate::interpret::{ }; // Returns a pointer to where the result lives -#[instrument(level = "trace", skip(ecx, body), ret)] -fn eval_body_using_ecx<'mir, 'tcx>( +#[instrument(level = "trace", skip(ecx, body))] +fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>( ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, cid: GlobalId<'tcx>, body: &'mir mir::Body<'tcx>, -) -> InterpResult<'tcx, MPlaceTy<'tcx>> { +) -> InterpResult<'tcx, R> { trace!(?ecx.param_env); let tcx = *ecx.tcx; assert!( @@ -87,7 +87,7 @@ fn eval_body_using_ecx<'mir, 'tcx>( // Since evaluation had no errors, validate the resulting constant. const_validate_mplace(&ecx, &ret, cid)?; - Ok(ret) + Ok(R::make_result(ret, ecx)) } /// The `InterpCx` is only meant to be used to do field and index projections into constants for @@ -294,14 +294,14 @@ pub trait InterpretationResult<'tcx> { /// evaluation query. fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self; } impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - _ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + _ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self { ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty } } @@ -352,41 +352,33 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); let res = ecx.load_mir(cid.instance.def, cid.promoted); - match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)) { - Err(error) => { - let (error, backtrace) = error.into_parts(); - backtrace.print_backtrace(); - - let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { - ("static", String::new()) + res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| { + let (error, backtrace) = error.into_parts(); + backtrace.print_backtrace(); + + let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { + ("static", String::new()) + } else { + // If the current item has generics, we'd like to enrich the message with the + // instance and its args: to show the actual compile-time values, in addition to + // the expression, leading to the const eval error. + let instance = &cid.instance; + if !instance.args.is_empty() { + let instance = with_no_trimmed_paths!(instance.to_string()); + ("const_with_path", instance) } else { - // If the current item has generics, we'd like to enrich the message with the - // instance and its args: to show the actual compile-time values, in addition to - // the expression, leading to the const eval error. - let instance = &cid.instance; - if !instance.args.is_empty() { - let instance = with_no_trimmed_paths!(instance.to_string()); - ("const_with_path", instance) - } else { - ("const", String::new()) - } - }; - - Err(super::report( - *ecx.tcx, - error, - None, - || super::get_span_and_frames(ecx.tcx, ecx.stack()), - |span, frames| ConstEvalError { - span, - error_kind: kind, - instance, - frame_notes: frames, - }, - )) - } - Ok(mplace) => Ok(R::make_result(mplace, ecx)), - } + ("const", String::new()) + } + }; + + super::report( + *ecx.tcx, + error, + None, + || super::get_span_and_frames(ecx.tcx, ecx.stack()), + |span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames }, + ) + }) } #[inline(always)] diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 10b5e3ff1df5a..c83ef14c03fe7 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -84,7 +84,7 @@ where impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { fn make_result<'mir>( mplace: MPlaceTy<'tcx>, - mut ecx: InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, ) -> Self { let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1; From a316c21dc8aa1ebfb961a2c789757593fd1db9ef Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 13 Mar 2024 09:25:20 +0000 Subject: [PATCH 68/79] Rename some things around validation error reporting to signal that it is in fact about validation failures --- compiler/rustc_const_eval/messages.ftl | 12 ++++++------ .../rustc_const_eval/src/const_eval/eval_queries.rs | 10 ++++++---- compiler/rustc_const_eval/src/errors.rs | 6 +++--- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index f3af633b4e561..0046190d20cc7 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -374,12 +374,6 @@ const_eval_unallowed_op_in_const_context = const_eval_unavailable_target_features_for_fn = calling a function that requires unavailable target features: {$unavailable_feats} -const_eval_undefined_behavior = - it is undefined behavior to use this value - -const_eval_undefined_behavior_note = - The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - const_eval_uninhabited_enum_variant_read = read discriminant of an uninhabited enum variant const_eval_uninhabited_enum_variant_written = @@ -434,6 +428,12 @@ const_eval_validation_expected_raw_ptr = expected a raw pointer const_eval_validation_expected_ref = expected a reference const_eval_validation_expected_str = expected a string +const_eval_validation_failure = + it is undefined behavior to use this value + +const_eval_validation_failure_note = + The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + const_eval_validation_front_matter_invalid_value = constructing invalid value const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path} diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index d62ab39d0ecfc..5a1c7cc4209ad 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -382,7 +382,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( } #[inline(always)] -pub fn const_validate_mplace<'mir, 'tcx>( +fn const_validate_mplace<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, mplace: &MPlaceTy<'tcx>, cid: GlobalId<'tcx>, @@ -402,7 +402,9 @@ pub fn const_validate_mplace<'mir, 'tcx>( } }; ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) - .map_err(|error| const_report_error(&ecx, error, alloc_id))?; + // Instead of just reporting the `InterpError` via the usual machinery, we give a more targetted + // error about the validation failure. + .map_err(|error| report_validation_error(&ecx, error, alloc_id))?; inner = true; } @@ -410,7 +412,7 @@ pub fn const_validate_mplace<'mir, 'tcx>( } #[inline(always)] -pub fn const_report_error<'mir, 'tcx>( +fn report_validation_error<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, error: InterpErrorInfo<'tcx>, alloc_id: AllocId, @@ -429,6 +431,6 @@ pub fn const_report_error<'mir, 'tcx>( error, None, || crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()), - move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes }, + move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes }, ) } diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 46790264359b7..cc32640408b7e 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -412,11 +412,11 @@ pub struct NullaryIntrinsicError { } #[derive(Diagnostic)] -#[diag(const_eval_undefined_behavior, code = E0080)] -pub struct UndefinedBehavior { +#[diag(const_eval_validation_failure, code = E0080)] +pub struct ValidationFailure { #[primary_span] pub span: Span, - #[note(const_eval_undefined_behavior_note)] + #[note(const_eval_validation_failure_note)] pub ub_note: Option<()>, #[subdiagnostic] pub frames: Vec, From ec0b459ad20cb10ad5bab36e57c6bb8a8795d024 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 14 Mar 2024 11:29:04 +0000 Subject: [PATCH 69/79] Update build instructions for OpenHarmony The platform page now recommends using rustup since the target is now tier 2. --- .../rustc/src/platform-support/openharmony.md | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/doc/rustc/src/platform-support/openharmony.md b/src/doc/rustc/src/platform-support/openharmony.md index 9f90e7413268b..b2ddbfdfa2918 100644 --- a/src/doc/rustc/src/platform-support/openharmony.md +++ b/src/doc/rustc/src/platform-support/openharmony.md @@ -96,9 +96,34 @@ exec /path/to/ohos-sdk/linux/native/llvm/bin/clang++ \ Future versions of the OpenHarmony SDK will avoid the need for this process. -## Building the target +## Building Rust programs + +Rustup ships pre-compiled artifacts for this target, which you can install with: +```sh +rustup target add aarch64-unknown-linux-ohos +rustup target add armv7-unknown-linux-ohos +rustup target add x86_64-unknown-linux-ohos +``` + +You will need to configure the linker to use in `~/.cargo/config.toml`: +```toml +[target.aarch64-unknown-linux-ohos] +ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" +linker = "/path/to/aarch64-unknown-linux-ohos-clang.sh" + +[target.armv7-unknown-linux-ohos] +ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" +linker = "/path/to/armv7-unknown-linux-ohos-clang.sh" + +[target.x86_64-unknown-linux-ohos] +ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" +linker = "/path/to/x86_64-unknown-linux-ohos-clang.sh" +``` -To build a rust toolchain, create a `config.toml` with the following contents: +## Building the target from source + +Instead of using `rustup`, you can instead build a rust toolchain from source. +Create a `config.toml` with the following contents: ```toml profile = "compiler" @@ -130,28 +155,6 @@ ranlib = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ranlib" linker = "/path/to/x86_64-unknown-linux-ohos-clang.sh" ``` -## Building Rust programs - -Rust does not yet ship pre-compiled artifacts for this target. To compile for -this target, you will either need to build Rust with the target enabled (see -"Building the target" above), or build your own copy of `core` by using -`build-std` or similar. - -You will need to configure the linker to use in `~/.cargo/config`: -```toml -[target.aarch64-unknown-linux-ohos] -ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" -linker = "/path/to/aarch64-unknown-linux-ohos-clang.sh" - -[target.armv7-unknown-linux-ohos] -ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" -linker = "/path/to/armv7-unknown-linux-ohos-clang.sh" - -[target.x86_64-unknown-linux-ohos] -ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" -linker = "/path/to/x86_64-unknown-linux-ohos-clang.sh" -``` - ## Testing Running the Rust testsuite is possible, but currently difficult due to the way From 24a1729566f21f0df2cc258f61adddd6be7c40b3 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 11 Jan 2024 13:02:50 +0100 Subject: [PATCH 70/79] eagerly instantiate binders to avoid relying on `sub` --- compiler/rustc_middle/src/traits/mod.rs | 1 + .../error_reporting/type_err_ctxt_ext.rs | 2 + .../src/traits/select/candidate_assembly.rs | 5 +- .../src/traits/select/confirmation.rs | 35 +++++--- .../src/traits/select/mod.rs | 27 ++++-- .../src/traits/vtable.rs | 1 + .../dedup-normalized-1.rs | 24 +++++ .../dedup-normalized-2-higher-ranked.rs | 27 ++++++ .../dedup-normalized-2-higher-ranked.stderr | 20 +++++ .../issue-110963-early.stderr | 27 ++---- tests/ui/closures/multiple-fn-bounds.stderr | 2 +- tests/ui/coroutine/resume-arg-late-bound.rs | 2 +- .../ui/coroutine/resume-arg-late-bound.stderr | 14 +-- .../bugs/issue-88382.stderr | 29 +++--- .../issue-93340-1.next.stderr | 53 +++++++++++ .../issue-93340-1.old.stderr | 20 +++++ .../generic-associated-types/issue-93340-1.rs | 22 +++++ .../{issue-93340.rs => issue-93340-2.rs} | 4 +- .../higher-ranked-lifetime-error.rs | 2 +- .../higher-ranked-lifetime-error.stderr | 9 +- .../higher-ranked/trait-bounds/issue-59311.rs | 8 +- .../trait-bounds/issue-59311.stderr | 14 +-- .../normalize-under-binder/issue-71955.rs | 8 +- .../normalize-under-binder/issue-71955.stderr | 75 ++++------------ tests/ui/implied-bounds/gluon_salsa.rs | 13 ++- tests/ui/lifetimes/issue-105675.rs | 12 +-- tests/ui/lifetimes/issue-105675.stderr | 89 ++++--------------- tests/ui/lifetimes/issue-79187-2.rs | 2 +- tests/ui/lifetimes/issue-79187-2.stderr | 22 +---- tests/ui/lifetimes/issue-79187.rs | 4 +- tests/ui/lifetimes/issue-79187.stderr | 24 ++--- .../lifetimes/lifetime-errors/issue_74400.rs | 2 +- .../lifetime-errors/issue_74400.stderr | 16 ++-- ...osure-arg-type-mismatch-issue-45727.stderr | 2 +- .../closure-arg-type-mismatch.rs | 2 +- .../closure-arg-type-mismatch.stderr | 19 ++-- tests/ui/mismatched_types/closure-mismatch.rs | 4 +- .../mismatched_types/closure-mismatch.stderr | 45 ++-------- .../ui/mismatched_types/fn-variance-1.stderr | 4 +- .../ui/mismatched_types/issue-36053-2.stderr | 2 +- ...uggest-option-asderef-inference-var.stderr | 2 +- .../suggest-option-asderef-unfixable.stderr | 4 +- .../suggest-option-asderef.stderr | 8 +- .../missing-universe-cause-issue-114907.rs | 4 +- ...missing-universe-cause-issue-114907.stderr | 47 +++------- tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs | 4 +- .../ui/rfcs/rfc-1623-static/rfc1623-2.stderr | 18 ++-- .../late-bound-in-borrow-closure-sugg.stderr | 2 +- .../typeck/mismatched-map-under-self.stderr | 2 +- 49 files changed, 389 insertions(+), 395 deletions(-) create mode 100644 tests/ui/associated-type-bounds/dedup-normalized-1.rs create mode 100644 tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs create mode 100644 tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr create mode 100644 tests/ui/generic-associated-types/issue-93340-1.next.stderr create mode 100644 tests/ui/generic-associated-types/issue-93340-1.old.stderr create mode 100644 tests/ui/generic-associated-types/issue-93340-1.rs rename tests/ui/generic-associated-types/{issue-93340.rs => issue-93340-2.rs} (80%) diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index aea5865335124..a04bd636622ea 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -618,6 +618,7 @@ pub enum SelectionError<'tcx> { OpaqueTypeAutoTraitLeakageUnknown(DefId), } +// FIXME(@lcnr): The `Binder` here should be unnecessary. Just use `TraitRef` instead. #[derive(Clone, Debug, TypeVisitable)] pub struct SignatureMismatchData<'tcx> { pub found_trait_ref: ty::PolyTraitRef<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index d18acb8c864ba..b85a05c774fa9 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -3409,6 +3409,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err) } + // FIXME(@lcnr): This function could be changed to trait `TraitRef` directly + // instead of using a `Binder`. fn report_signature_mismatch_error( &self, obligation: &PredicateObligation<'tcx>, 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 89654ed61aeb9..49091e53be713 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -165,7 +165,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate); let placeholder_trait_predicate = self.infcx.enter_forall_and_leak_universe(poly_trait_predicate); - debug!(?placeholder_trait_predicate); // The bounds returned by `item_bounds` may contain duplicates after // normalization, so try to deduplicate when possible to avoid @@ -184,8 +183,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { selcx.infcx.probe(|_| { match selcx.match_normalize_trait_ref( obligation, - bound.to_poly_trait_ref(), placeholder_trait_predicate.trait_ref, + bound.to_poly_trait_ref(), ) { Ok(None) => { candidates.vec.push(ProjectionCandidate(idx)); @@ -881,8 +880,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.probe(|_| { self.match_normalize_trait_ref( obligation, - upcast_trait_ref, placeholder_trait_predicate.trait_ref, + upcast_trait_ref, ) .is_ok() }) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 70f6b240ab7e5..51fc223a5d1b3 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -9,7 +9,7 @@ use rustc_ast::Mutability; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; +use rustc_infer::infer::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{ @@ -161,8 +161,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let placeholder_trait_predicate = self.infcx.enter_forall_and_leak_universe(trait_predicate).trait_ref; let placeholder_self_ty = placeholder_trait_predicate.self_ty(); - let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate); - let candidate_predicate = self .for_each_item_bound( placeholder_self_ty, @@ -182,6 +180,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .expect("projection candidate is not a trait predicate") .map_bound(|t| t.trait_ref); + let candidate = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + candidate, + ); let mut obligations = Vec::new(); let candidate = normalize_with_depth_to( self, @@ -195,7 +198,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligations.extend( self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate) + .eq(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate) .map(|InferOk { obligations, .. }| obligations) .map_err(|_| Unimplemented)?, ); @@ -499,7 +502,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let trait_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty()); - let obligation_trait_ref = ty::Binder::dummy(trait_predicate.trait_ref); let ty::Dynamic(data, ..) = *self_ty.kind() else { span_bug!(obligation.cause.span, "object candidate with non-object"); }; @@ -520,19 +522,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let unnormalized_upcast_trait_ref = supertraits.nth(index).expect("supertraits iterator no longer has as many elements"); + let upcast_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + unnormalized_upcast_trait_ref, + ); let upcast_trait_ref = normalize_with_depth_to( self, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - unnormalized_upcast_trait_ref, + upcast_trait_ref, &mut nested, ); nested.extend( self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, obligation_trait_ref, upcast_trait_ref) + .eq(DefineOpaqueTypes::No, trait_predicate.trait_ref, upcast_trait_ref) .map(|InferOk { obligations, .. }| obligations) .map_err(|_| Unimplemented)?, ); @@ -1021,7 +1028,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &PolyTraitObligation<'tcx>, self_ty_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result>, SelectionError<'tcx>> { - let obligation_trait_ref = obligation.predicate.to_poly_trait_ref(); + let obligation_trait_ref = + self.infcx.enter_forall_and_leak_universe(obligation.predicate.to_poly_trait_ref()); + let self_ty_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + self_ty_trait_ref, + ); // Normalize the obligation and expected trait refs together, because why not let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } = ensure_sufficient_stack(|| { @@ -1037,15 +1050,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) + .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) .map(|InferOk { mut obligations, .. }| { obligations.extend(nested); obligations }) .map_err(|terr| { SignatureMismatch(Box::new(SignatureMismatchData { - expected_trait_ref: obligation_trait_ref, - found_trait_ref: expected_trait_ref, + expected_trait_ref: ty::Binder::dummy(obligation_trait_ref), + found_trait_ref: ty::Binder::dummy(expected_trait_ref), terr, })) }) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index a6bd1ba9c3f88..be748de1b17e1 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -33,6 +33,7 @@ use rustc_errors::{Diag, EmissionGuarantee}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::BoundRegionConversionTime; +use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::TraitObligation; use rustc_middle::dep_graph::dep_kinds; @@ -1651,15 +1652,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn match_normalize_trait_ref( &mut self, obligation: &PolyTraitObligation<'tcx>, - trait_bound: ty::PolyTraitRef<'tcx>, placeholder_trait_ref: ty::TraitRef<'tcx>, - ) -> Result>, ()> { + trait_bound: ty::PolyTraitRef<'tcx>, + ) -> Result>, ()> { debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); if placeholder_trait_ref.def_id != trait_bound.def_id() { // Avoid unnecessary normalization return Err(()); } + let trait_bound = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + trait_bound, + ); let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| { normalize_with_depth( self, @@ -1671,7 +1677,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }); self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, ty::Binder::dummy(placeholder_trait_ref), trait_bound) + .eq(DefineOpaqueTypes::No, placeholder_trait_ref, trait_bound) .map(|InferOk { obligations: _, value: () }| { // This method is called within a probe, so we can't have // inference variables and placeholders escape. @@ -1683,7 +1689,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) .map_err(|_| ()) } - fn where_clause_may_apply<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, @@ -1733,7 +1738,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let is_match = self .infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, obligation.predicate, infer_projection) + .eq(DefineOpaqueTypes::No, obligation.predicate, infer_projection) .is_ok_and(|InferOk { obligations, value: () }| { self.evaluate_predicates_recursively( TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), @@ -2533,7 +2538,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { nested.extend( self.infcx .at(&obligation.cause, obligation.param_env) - .sup( + .eq( DefineOpaqueTypes::No, upcast_principal.map_bound(|trait_ref| { ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) @@ -2571,7 +2576,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { nested.extend( self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, source_projection, target_projection) + .eq(DefineOpaqueTypes::No, source_projection, target_projection) .map_err(|_| SelectionError::Unimplemented)? .into_obligations(), ); @@ -2615,9 +2620,15 @@ impl<'tcx> SelectionContext<'_, 'tcx> { obligation: &PolyTraitObligation<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result>, ()> { + let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + poly_trait_ref, + ); self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, obligation.predicate.to_poly_trait_ref(), poly_trait_ref) + .eq(DefineOpaqueTypes::No, predicate.trait_ref, trait_ref) .map(|InferOk { obligations, .. }| obligations) .map_err(|_| ()) } diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 3c0316fce171d..46a68508753c0 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -320,6 +320,7 @@ fn vtable_entries<'tcx>( } /// Find slot base for trait methods within vtable entries of another trait +// FIXME(@lcnr): This isn't a query, so why does it take a tuple as its argument. pub(super) fn vtable_trait_first_method_offset<'tcx>( tcx: TyCtxt<'tcx>, key: ( diff --git a/tests/ui/associated-type-bounds/dedup-normalized-1.rs b/tests/ui/associated-type-bounds/dedup-normalized-1.rs new file mode 100644 index 0000000000000..5329018e79f2a --- /dev/null +++ b/tests/ui/associated-type-bounds/dedup-normalized-1.rs @@ -0,0 +1,24 @@ +//@ check-pass + +// We try to prove `T::Rigid: Into` and have 2 candidates from where-clauses: +// +// - `Into` +// - `Into<::Assoc>` +// +// This causes ambiguity unless we normalize the alias in the second candidate +// to detect that they actually result in the same constraints. +trait Trait { + type Rigid: Elaborate + Into; +} + +trait Elaborate: Into { + type Assoc; +} + +fn impls, U>(_: T) {} + +fn test(rigid: P::Rigid) { + impls(rigid); +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs new file mode 100644 index 0000000000000..9224d47d30fd6 --- /dev/null +++ b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs @@ -0,0 +1,27 @@ +// We try to prove `for<'b> T::Rigid: Bound<'b, ?0>` and have 2 candidates from where-clauses: +// +// - `for<'a> Bound<'a, String>` +// - `for<'a> Bound<'a, ::Assoc>` +// +// This causes ambiguity unless we normalize the alias in the second candidate +// to detect that they actually result in the same constraints. We currently +// fail to detect that the constraints from these bounds are equal and error +// with ambiguity. +trait Bound<'a, U> {} + +trait Trait { + type Rigid: Elaborate + for<'a> Bound<'a, String>; +} + +trait Elaborate: for<'a> Bound<'a, Self::Assoc> { + type Assoc; +} + +fn impls Bound<'b, U>, U>(_: T) {} + +fn test(rigid: P::Rigid) { + impls(rigid); + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr new file mode 100644 index 0000000000000..372d379de5a4f --- /dev/null +++ b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr @@ -0,0 +1,20 @@ +error[E0283]: type annotations needed + --> $DIR/dedup-normalized-2-higher-ranked.rs:23:5 + | +LL | impls(rigid); + | ^^^^^ cannot infer type of the type parameter `U` declared on the function `impls` + | + = note: cannot satisfy `for<'b>

::Rigid: Bound<'b, _>` +note: required by a bound in `impls` + --> $DIR/dedup-normalized-2-higher-ranked.rs:20:13 + | +LL | fn impls Bound<'b, U>, U>(_: T) {} + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls` +help: consider specifying the generic arguments + | +LL | impls::<

::Rigid, U>(rigid); + | ++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/async-await/return-type-notation/issue-110963-early.stderr b/tests/ui/async-await/return-type-notation/issue-110963-early.stderr index feae2698e8faa..23ede089b5a8b 100644 --- a/tests/ui/async-await/return-type-notation/issue-110963-early.stderr +++ b/tests/ui/async-await/return-type-notation/issue-110963-early.stderr @@ -7,7 +7,7 @@ LL | #![feature(return_type_notation)] = note: see issue #109417 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0308]: mismatched types +error: implementation of `Send` is not general enough --> $DIR/issue-110963-early.rs:14:5 | LL | / spawn(async move { @@ -16,17 +16,12 @@ LL | | if !hc.check().await { LL | | log_health_check_failure().await; LL | | } LL | | }); - | |______^ one type is more general than the other + | |______^ implementation of `Send` is not general enough | - = note: expected trait `Send` - found trait `for<'a> Send` -note: the lifetime requirement is introduced here - --> $DIR/issue-110963-early.rs:34:17 - | -LL | F: Future + Send + 'static, - | ^^^^ + = note: `Send` would have to be implemented for the type `impl Future { ::check<'0>() }`, for any two lifetimes `'0` and `'1`... + = note: ...but `Send` is actually implemented for the type `impl Future { ::check<'2>() }`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Send` is not general enough --> $DIR/issue-110963-early.rs:14:5 | LL | / spawn(async move { @@ -35,17 +30,11 @@ LL | | if !hc.check().await { LL | | log_health_check_failure().await; LL | | } LL | | }); - | |______^ one type is more general than the other - | - = note: expected trait `Send` - found trait `for<'a> Send` -note: the lifetime requirement is introduced here - --> $DIR/issue-110963-early.rs:34:17 + | |______^ implementation of `Send` is not general enough | -LL | F: Future + Send + 'static, - | ^^^^ + = note: `Send` would have to be implemented for the type `impl Future { ::check<'0>() }`, for any two lifetimes `'0` and `'1`... + = note: ...but `Send` is actually implemented for the type `impl Future { ::check<'2>() }`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/closures/multiple-fn-bounds.stderr b/tests/ui/closures/multiple-fn-bounds.stderr index 9a49fc99ac31a..861b39b4d076e 100644 --- a/tests/ui/closures/multiple-fn-bounds.stderr +++ b/tests/ui/closures/multiple-fn-bounds.stderr @@ -8,7 +8,7 @@ LL | foo(move |x| v); | expected due to this | = note: expected closure signature `fn(_) -> _` - found closure signature `for<'a> fn(&'a _) -> _` + found closure signature `fn(&_) -> _` note: closure inferred to have a different signature due to this bound --> $DIR/multiple-fn-bounds.rs:1:11 | diff --git a/tests/ui/coroutine/resume-arg-late-bound.rs b/tests/ui/coroutine/resume-arg-late-bound.rs index dd6d318afbcd7..3c2ab41047e3d 100644 --- a/tests/ui/coroutine/resume-arg-late-bound.rs +++ b/tests/ui/coroutine/resume-arg-late-bound.rs @@ -13,5 +13,5 @@ fn main() { *arg = true; }; test(gen); - //~^ ERROR mismatched types + //~^ ERROR implementation of `Coroutine` is not general enough } diff --git a/tests/ui/coroutine/resume-arg-late-bound.stderr b/tests/ui/coroutine/resume-arg-late-bound.stderr index a97cc6190fd91..4a4ee08c529ec 100644 --- a/tests/ui/coroutine/resume-arg-late-bound.stderr +++ b/tests/ui/coroutine/resume-arg-late-bound.stderr @@ -1,17 +1,11 @@ -error[E0308]: mismatched types +error: implementation of `Coroutine` is not general enough --> $DIR/resume-arg-late-bound.rs:15:5 | LL | test(gen); - | ^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^ implementation of `Coroutine` is not general enough | - = note: expected trait `for<'a> Coroutine<&'a mut bool>` - found trait `Coroutine<&mut bool>` -note: the lifetime requirement is introduced here - --> $DIR/resume-arg-late-bound.rs:8:17 - | -LL | fn test(a: impl for<'a> Coroutine<&'a mut bool>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `{coroutine@$DIR/resume-arg-late-bound.rs:11:15: 11:31}` must implement `Coroutine<&'1 mut bool>`, for any lifetime `'1`... + = note: ...but it actually implements `Coroutine<&'2 mut bool>`, for some specific lifetime `'2` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/generic-associated-types/bugs/issue-88382.stderr b/tests/ui/generic-associated-types/bugs/issue-88382.stderr index 9b061528e3beb..0f5e394ab6155 100644 --- a/tests/ui/generic-associated-types/bugs/issue-88382.stderr +++ b/tests/ui/generic-associated-types/bugs/issue-88382.stderr @@ -1,26 +1,21 @@ -error[E0631]: type mismatch in function arguments +error[E0283]: type annotations needed --> $DIR/issue-88382.rs:26:40 | LL | do_something(SomeImplementation(), test); - | ------------ ^^^^ expected due to this - | | - | required by a bound introduced by this call -... -LL | fn test<'a, I: Iterable>(_: &mut I::Iterator<'a>) {} - | ------------------------------------------------- found signature defined here + | ^^^^ cannot infer type of the type parameter `I` declared on the function `test` | - = note: expected function signature `for<'a> fn(&'a mut std::iter::Empty) -> _` - found function signature `for<'a, 'b> fn(&'b mut <_ as Iterable>::Iterator<'a>) -> _` -note: required by a bound in `do_something` - --> $DIR/issue-88382.rs:20:48 + = note: cannot satisfy `_: Iterable` + = help: the trait `Iterable` is implemented for `SomeImplementation` +note: required by a bound in `test` + --> $DIR/issue-88382.rs:29:16 | -LL | fn do_something(i: I, mut f: impl for<'a> Fn(&mut I::Iterator<'a>)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `do_something` -help: consider wrapping the function in a closure +LL | fn test<'a, I: Iterable>(_: &mut I::Iterator<'a>) {} + | ^^^^^^^^ required by this bound in `test` +help: consider specifying the generic argument | -LL | do_something(SomeImplementation(), |arg0: &mut std::iter::Empty| test(/* &mut <_ as Iterable>::Iterator<'_> */)); - | ++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ +LL | do_something(SomeImplementation(), test::); + | +++++ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0631`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/generic-associated-types/issue-93340-1.next.stderr b/tests/ui/generic-associated-types/issue-93340-1.next.stderr new file mode 100644 index 0000000000000..0a056c9857aaf --- /dev/null +++ b/tests/ui/generic-associated-types/issue-93340-1.next.stderr @@ -0,0 +1,53 @@ +error[E0283]: type annotations needed + --> $DIR/issue-93340-1.rs:16:5 + | +LL | cmp_eq + | ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq` + | + = note: cannot satisfy `_: Scalar` +note: required by a bound in `cmp_eq` + --> $DIR/issue-93340-1.rs:9:22 + | +LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { + | ^^^^^^ required by this bound in `cmp_eq` +help: consider specifying the generic arguments + | +LL | cmp_eq:: + | +++++++++++ + +error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` + --> $DIR/issue-93340-1.rs:16:5 + | +LL | cmp_eq + | ^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_93340_1`) + +error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` + --> $DIR/issue-93340-1.rs:16:5 + | +LL | cmp_eq + | ^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_93340_1`) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0275]: overflow evaluating the requirement `for<'a, 'b> fn(::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::} <: ...` + --> $DIR/issue-93340-1.rs:14:51 + | +LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { + | ___________________________________________________^ +LL | | +LL | | cmp_eq +LL | | +LL | | +LL | | +LL | | } + | |_^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_93340_1`) + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0275, E0283. +For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/generic-associated-types/issue-93340-1.old.stderr b/tests/ui/generic-associated-types/issue-93340-1.old.stderr new file mode 100644 index 0000000000000..8b6ab16bb0a25 --- /dev/null +++ b/tests/ui/generic-associated-types/issue-93340-1.old.stderr @@ -0,0 +1,20 @@ +error[E0283]: type annotations needed + --> $DIR/issue-93340-1.rs:16:5 + | +LL | cmp_eq + | ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq` + | + = note: cannot satisfy `_: Scalar` +note: required by a bound in `cmp_eq` + --> $DIR/issue-93340-1.rs:9:22 + | +LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { + | ^^^^^^ required by this bound in `cmp_eq` +help: consider specifying the generic arguments + | +LL | cmp_eq:: + | +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/generic-associated-types/issue-93340-1.rs b/tests/ui/generic-associated-types/issue-93340-1.rs new file mode 100644 index 0000000000000..4d8ea9d8d4811 --- /dev/null +++ b/tests/ui/generic-associated-types/issue-93340-1.rs @@ -0,0 +1,22 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +pub trait Scalar: 'static { + type RefType<'a>: ScalarRef<'a>; +} + +pub trait ScalarRef<'a>: 'a {} + +fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { + todo!() +} + +fn build_expression( +) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { + //[next]~^ ERROR overflow evaluating the requirement + cmp_eq + //~^ ERROR type annotations needed + //[next]~| ERROR overflow evaluating the requirement + //[next]~| ERROR overflow evaluating the requirement +} + +fn main() {} diff --git a/tests/ui/generic-associated-types/issue-93340.rs b/tests/ui/generic-associated-types/issue-93340-2.rs similarity index 80% rename from tests/ui/generic-associated-types/issue-93340.rs rename to tests/ui/generic-associated-types/issue-93340-2.rs index 783f8c06ebfdf..b55ca845cd3f2 100644 --- a/tests/ui/generic-associated-types/issue-93340.rs +++ b/tests/ui/generic-associated-types/issue-93340-2.rs @@ -1,3 +1,5 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver //@ check-pass pub trait Scalar: 'static { @@ -12,7 +14,7 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT fn build_expression( ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { - cmp_eq + cmp_eq:: } fn main() {} diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs b/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs index aee5db8366948..f89a37c851217 100644 --- a/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs @@ -10,5 +10,5 @@ fn id(x: &String) -> &String { fn main() { assert_all::<_, &String>(id); - //~^ mismatched types + //~^ ERROR implementation of `FnMut` is not general enough } diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr index c25e731d9627c..d7add865aa065 100644 --- a/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr @@ -1,12 +1,11 @@ -error[E0308]: mismatched types +error: implementation of `FnMut` is not general enough --> $DIR/higher-ranked-lifetime-error.rs:12:5 | LL | assert_all::<_, &String>(id); - | ^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnMut` is not general enough | - = note: expected trait `for<'a> fn(&'a String) -> &'a String {id} as FnMut<(&'a String,)>>` - found trait `for<'a> fn(&'a String) -> &'a String {id} as FnMut<(&'a String,)>>` + = note: `for<'a> fn(&'a String) -> &'a String {id}` must implement `FnMut<(&String,)>` + = note: ...but it actually implements `FnMut<(&'0 String,)>`, for some specific lifetime `'0` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/higher-ranked/trait-bounds/issue-59311.rs b/tests/ui/higher-ranked/trait-bounds/issue-59311.rs index 387c78a802a72..4e722dc0e8009 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-59311.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-59311.rs @@ -6,17 +6,17 @@ // an error, but the regression test is here to ensure // that it does not ICE. See discussion on #74889 for details. -pub trait T { +pub trait Trait { fn t(&self, _: F) {} } pub fn crash(v: &V) where - for<'a> &'a V: T + 'static, + for<'a> &'a V: Trait + 'static, { v.t(|| {}); - //~^ ERROR: higher-ranked lifetime error - //~| ERROR: higher-ranked lifetime error + //~^ ERROR: implementation of `Trait` is not general enough + //~| ERROR: implementation of `Trait` is not general enough //~| ERROR: higher-ranked lifetime error } diff --git a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr index 3053a29980260..f8bed86ccf599 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr +++ b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr @@ -1,18 +1,20 @@ -error: higher-ranked lifetime error +error: implementation of `Trait` is not general enough --> $DIR/issue-59311.rs:17:5 | LL | v.t(|| {}); - | ^^^^^^^^^^ + | ^^^^^^^^^^ implementation of `Trait` is not general enough | - = note: could not prove `{closure@$DIR/issue-59311.rs:17:9: 17:11} well-formed` + = note: `Trait` would have to be implemented for the type `&'a V` + = note: ...but `Trait` is actually implemented for the type `&'0 V`, for some specific lifetime `'0` -error: higher-ranked lifetime error +error: implementation of `Trait` is not general enough --> $DIR/issue-59311.rs:17:5 | LL | v.t(|| {}); - | ^^^^^^^^^^ + | ^^^^^^^^^^ implementation of `Trait` is not general enough | - = note: could not prove `{closure@$DIR/issue-59311.rs:17:9: 17:11} well-formed` + = note: `Trait` would have to be implemented for the type `&'a V` + = note: ...but `Trait` is actually implemented for the type `&'0 V`, for some specific lifetime `'0` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: higher-ranked lifetime error diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs index 4bd3b96e47581..a44ed9e5ef59b 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs @@ -43,9 +43,9 @@ fn main() { } foo(bar, "string", |s| s.len() == 5); - //~^ ERROR mismatched types - //~| ERROR mismatched types + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough foo(baz, "string", |s| s.0.len() == 5); - //~^ ERROR mismatched types - //~| ERROR mismatched types + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough } diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr index 1cf364aa9f6a1..b2bb417a8f01b 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr @@ -1,79 +1,40 @@ -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: expected trait `for<'a, 'b> FnOnce(&'a &'b str)` - found trait `for<'a> FnOnce(&'a &str)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-71955.rs:45:24 - | -LL | foo(bar, "string", |s| s.len() == 5); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-71955.rs:25:9 - | -LL | F2: FnOnce(&::Output) -> bool - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a, 'b> FnOnce(&'a &'b str)` - found trait `for<'a> FnOnce(&'a &str)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-71955.rs:45:24 - | -LL | foo(bar, "string", |s| s.len() == 5); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-71955.rs:25:44 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | F2: FnOnce(&::Output) -> bool - | ^^^^ + = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: expected trait `for<'a, 'b> FnOnce(&'a Wrapper<'b>)` - found trait `for<'a> FnOnce(&'a Wrapper<'_>)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-71955.rs:48:24 - | -LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-71955.rs:25:9 - | -LL | F2: FnOnce(&::Output) -> bool - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a, 'b> FnOnce(&'a Wrapper<'b>)` - found trait `for<'a> FnOnce(&'a Wrapper<'_>)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-71955.rs:48:24 - | -LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-71955.rs:25:44 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | F2: FnOnce(&::Output) -> bool - | ^^^^ + = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/implied-bounds/gluon_salsa.rs b/tests/ui/implied-bounds/gluon_salsa.rs index 368fb1979098f..cc6352c4a32e9 100644 --- a/tests/ui/implied-bounds/gluon_salsa.rs +++ b/tests/ui/implied-bounds/gluon_salsa.rs @@ -1,5 +1,6 @@ //@ check-pass -// Found in a crater run on #118553 +// Related to Bevy regression #115559, found in +// a crater run on #118553. pub trait QueryBase { type Db; @@ -17,11 +18,17 @@ pub struct QueryTable<'me, Q, DB> { _marker: Option<&'me ()>, } -impl<'me, Q> QueryTable<'me, Q, ::Db> // projection is important -// ^^^ removing 'me (and in QueryTable) gives a different error +impl<'me, Q> QueryTable<'me, Q, ::Db> where Q: for<'f> AsyncQueryFunction<'f>, { + // When borrowchechking this function we normalize `::Db` in the + // function signature to `>::SendDb`, where `'?x` is an + // unconstrained region variable. We then addd `>::SendDb: 'a` + // as an implied bound. We currently a structural equality to decide whether this bound + // should be used to prove the bound `>::SendDb: 'a`. For this + // to work we may have to structurally resolve regions as the actually used vars may + // otherwise be semantically equal but structurally different. pub fn get_async<'a>(&'a mut self) { panic!(); } diff --git a/tests/ui/lifetimes/issue-105675.rs b/tests/ui/lifetimes/issue-105675.rs index 58d8be8b65f78..2e2eaca0d335c 100644 --- a/tests/ui/lifetimes/issue-105675.rs +++ b/tests/ui/lifetimes/issue-105675.rs @@ -3,12 +3,12 @@ fn thing(x: impl FnOnce(&u32, &u32, u32)) {} fn main() { let f = | _ , y: &u32 , z | (); thing(f); - //~^ ERROR mismatched types - //~^^ ERROR mismatched types + //~^ ERROR implementation of `FnOnce` is not general enough + //~^^ ERROR implementation of `FnOnce` is not general enough let f = | x, y: _ , z: u32 | (); thing(f); - //~^ ERROR mismatched types - //~^^ ERROR mismatched types - //~^^^ ERROR implementation of `FnOnce` is not general enough - //~^^^^ ERROR implementation of `FnOnce` is not general enough + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough } diff --git a/tests/ui/lifetimes/issue-105675.stderr b/tests/ui/lifetimes/issue-105675.stderr index f1fa5a5986060..4b3d0e8ac5efc 100644 --- a/tests/ui/lifetimes/issue-105675.stderr +++ b/tests/ui/lifetimes/issue-105675.stderr @@ -1,91 +1,39 @@ -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-105675.rs:5:5 | LL | thing(f); - | ^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a, 'b> FnOnce(&'a u32, &'b u32, u32)` - found trait `for<'a> FnOnce(&u32, &'a u32, u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-105675.rs:4:13 - | -LL | let f = | _ , y: &u32 , z | (); - | ^^^^^^^^^^^^^^^^^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-105675.rs:1:18 - | -LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ -help: consider specifying the type of the closure parameters + | ^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | let f = |_: &_, y: &u32, z| (); - | ~~~~~~~~~~~~~~~~~~~ + = note: closure with signature `for<'a> fn(&'2 u32, &'a u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-105675.rs:5:5 | LL | thing(f); - | ^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a, 'b> FnOnce(&'a u32, &'b u32, u32)` - found trait `for<'a> FnOnce(&u32, &'a u32, u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-105675.rs:4:13 - | -LL | let f = | _ , y: &u32 , z | (); - | ^^^^^^^^^^^^^^^^^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-105675.rs:1:18 + | ^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: closure with signature `for<'a> fn(&'2 u32, &'a u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-105675.rs:9:5 | LL | thing(f); - | ^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a, 'b> FnOnce(&'a u32, &'b u32, u32)` - found trait `FnOnce(&u32, &u32, u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-105675.rs:8:13 - | -LL | let f = | x, y: _ , z: u32 | (); - | ^^^^^^^^^^^^^^^^^^^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-105675.rs:1:18 - | -LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ -help: consider specifying the type of the closure parameters + | ^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | let f = |x: &_, y: &_, z: u32| (); - | ~~~~~~~~~~~~~~~~~~~~~~ + = note: closure with signature `fn(&'2 u32, &u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-105675.rs:9:5 | LL | thing(f); - | ^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a, 'b> FnOnce(&'a u32, &'b u32, u32)` - found trait `FnOnce(&u32, &u32, u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-105675.rs:8:13 - | -LL | let f = | x, y: _ , z: u32 | (); - | ^^^^^^^^^^^^^^^^^^^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-105675.rs:1:18 - | -LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider specifying the type of the closure parameters + | ^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | let f = |x: &_, y: &_, z: u32| (); - | ~~~~~~~~~~~~~~~~~~~~~~ + = note: closure with signature `fn(&u32, &'2 u32, u32)` must implement `FnOnce<(&u32, &'1 u32, u32)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&u32, &'2 u32, u32)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/issue-105675.rs:9:5 @@ -95,6 +43,7 @@ LL | thing(f); | = note: closure with signature `fn(&'2 u32, &u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: implementation of `FnOnce` is not general enough --> $DIR/issue-105675.rs:9:5 @@ -104,7 +53,7 @@ LL | thing(f); | = note: closure with signature `fn(&u32, &'2 u32, u32)` must implement `FnOnce<(&u32, &'1 u32, u32)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&u32, &'2 u32, u32)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/lifetimes/issue-79187-2.rs b/tests/ui/lifetimes/issue-79187-2.rs index fff92c30b3751..9d7f17e7f2382 100644 --- a/tests/ui/lifetimes/issue-79187-2.rs +++ b/tests/ui/lifetimes/issue-79187-2.rs @@ -7,7 +7,7 @@ fn take_foo(_: impl Foo) {} fn main() { take_foo(|a| a); //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR mismatched types + //~| ERROR implementation of `Fn` is not general enough take_foo(|a: &i32| a); //~^ ERROR lifetime may not live long enough //~| ERROR mismatched types diff --git a/tests/ui/lifetimes/issue-79187-2.stderr b/tests/ui/lifetimes/issue-79187-2.stderr index e8115bb6b064f..78f6ce882dfab 100644 --- a/tests/ui/lifetimes/issue-79187-2.stderr +++ b/tests/ui/lifetimes/issue-79187-2.stderr @@ -25,28 +25,14 @@ LL | take_foo(|a| a); = note: closure with signature `fn(&'2 i32) -> &i32` must implement `FnOnce<(&'1 i32,)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 i32,)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/issue-79187-2.rs:8:5 | LL | take_foo(|a| a); - | ^^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a> Fn(&'a i32)` - found trait `Fn(&i32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-79187-2.rs:8:14 - | -LL | take_foo(|a| a); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-79187-2.rs:5:21 - | -LL | fn take_foo(_: impl Foo) {} - | ^^^ -help: consider specifying the type of the closure parameters + | ^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough | -LL | take_foo(|a: &_| a); - | ~~~~~~~ + = note: closure with signature `fn(&'2 i32) -> &i32` must implement `Fn<(&'1 i32,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 i32,)>`, for some specific lifetime `'2` error[E0308]: mismatched types --> $DIR/issue-79187-2.rs:11:5 diff --git a/tests/ui/lifetimes/issue-79187.rs b/tests/ui/lifetimes/issue-79187.rs index 8e13045623b3d..a8829bd4e4905 100644 --- a/tests/ui/lifetimes/issue-79187.rs +++ b/tests/ui/lifetimes/issue-79187.rs @@ -3,6 +3,6 @@ fn thing(x: impl FnOnce(&u32)) {} fn main() { let f = |_| (); thing(f); - //~^ ERROR mismatched types - //~^^ ERROR implementation of `FnOnce` is not general enough + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough } diff --git a/tests/ui/lifetimes/issue-79187.stderr b/tests/ui/lifetimes/issue-79187.stderr index 14bdfe75c08be..8adde8d6dfbf3 100644 --- a/tests/ui/lifetimes/issue-79187.stderr +++ b/tests/ui/lifetimes/issue-79187.stderr @@ -1,25 +1,11 @@ -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-79187.rs:5:5 | LL | thing(f); - | ^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a> FnOnce(&'a u32)` - found trait `FnOnce(&u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-79187.rs:4:13 - | -LL | let f = |_| (); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-79187.rs:1:18 - | -LL | fn thing(x: impl FnOnce(&u32)) {} - | ^^^^^^^^^^^^ -help: consider specifying the type of the closure parameters + | ^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | let f = |_: &_| (); - | ~~~~~~~ + = note: closure with signature `fn(&'2 u32)` must implement `FnOnce<(&'1 u32,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 u32,)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/issue-79187.rs:5:5 @@ -29,7 +15,7 @@ LL | thing(f); | = note: closure with signature `fn(&'2 u32)` must implement `FnOnce<(&'1 u32,)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 u32,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs index f17e0a678c96b..b02e38bec3b3e 100644 --- a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs +++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs @@ -13,6 +13,6 @@ fn g(data: &[T]) { //~^ ERROR the parameter type //~| ERROR the parameter type //~| ERROR the parameter type - //~| ERROR mismatched types //~| ERROR implementation of `FnOnce` is not general + //~| ERROR implementation of `Fn` is not general enough } diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr b/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr index beb838d2ff832..4dada6ff014ad 100644 --- a/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr +++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr @@ -42,19 +42,14 @@ help: consider adding an explicit lifetime bound LL | fn g(data: &[T]) { | +++++++++ -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/issue_74400.rs:12:5 | LL | f(data, identity) - | ^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a> Fn(&'a T)` - found trait `Fn(&T)` -note: the lifetime requirement is introduced here - --> $DIR/issue_74400.rs:8:34 - | -LL | fn f(data: &[T], key: impl Fn(&T) -> S) { - | ^^^^^^^^^^^ + = note: `fn(&'2 T) -> &'2 T {identity::<&'2 T>}` must implement `Fn<(&'1 T,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 T,)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/issue_74400.rs:12:5 @@ -67,5 +62,4 @@ LL | f(data, identity) error: aborting due to 5 previous errors -Some errors have detailed explanations: E0308, E0310. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr index 452cba6b4de70..e52e095e9f729 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr @@ -24,7 +24,7 @@ LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); | expected due to this | = note: expected closure signature `for<'a> fn(&'a {integer}) -> _` - found closure signature `for<'a, 'b, 'c> fn(&'a &'b &'c i32) -> _` + found closure signature `fn(&&&i32) -> _` note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL help: consider adjusting the signature so it does not borrow its argument diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch.rs index e73a33dfded7c..55a4a0b5707ff 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch.rs @@ -8,7 +8,7 @@ fn main() { fn baz(_: F) {} fn _test<'a>(f: fn(*mut &'a u32)) { baz(f); - //~^ ERROR: mismatched types + //~^ ERROR: implementation of `FnOnce` is not general enough //~| ERROR: borrowed data escapes //~| ERROR: not general enough } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch.stderr index e63d3f6a075db..abc5d150a3f9c 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch.stderr @@ -24,7 +24,7 @@ LL | a.iter().map(|_: &(u16, u16)| 45); | expected due to this | = note: expected closure signature `fn(&(u32, u32)) -> _` - found closure signature `for<'a> fn(&'a (u16, u16)) -> _` + found closure signature `fn(&(u16, u16)) -> _` note: required by a bound in `map` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL @@ -63,19 +63,14 @@ note: due to current limitations in the borrow checker, this implies a `'static` LL | fn baz(_: F) {} | ^^^^^^^^^^^^^ -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/closure-arg-type-mismatch.rs:10:5 | LL | baz(f); - | ^^^^^^ one type is more general than the other + | ^^^^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a> Fn(*mut &'a u32)` - found trait `Fn(*mut &u32)` -note: the lifetime requirement is introduced here - --> $DIR/closure-arg-type-mismatch.rs:8:11 - | -LL | fn baz(_: F) {} - | ^^^^^^^^^^^^^ + = note: `fn(*mut &'2 u32)` must implement `Fn<(*mut &'1 u32,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(*mut &'2 u32,)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/closure-arg-type-mismatch.rs:10:5 @@ -88,5 +83,5 @@ LL | baz(f); error: aborting due to 6 previous errors -Some errors have detailed explanations: E0308, E0521, E0631. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0521, E0631. +For more information about an error, try `rustc --explain E0521`. diff --git a/tests/ui/mismatched_types/closure-mismatch.rs b/tests/ui/mismatched_types/closure-mismatch.rs index 4eb33497c3956..efaed4dc1b99d 100644 --- a/tests/ui/mismatched_types/closure-mismatch.rs +++ b/tests/ui/mismatched_types/closure-mismatch.rs @@ -7,8 +7,8 @@ fn baz(_: T) {} fn main() { baz(|_| ()); //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR mismatched types + //~| ERROR implementation of `Fn` is not general enough baz(|x| ()); //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR mismatched types + //~| ERROR implementation of `Fn` is not general enough } diff --git a/tests/ui/mismatched_types/closure-mismatch.stderr b/tests/ui/mismatched_types/closure-mismatch.stderr index 74033c1857377..802110c6511de 100644 --- a/tests/ui/mismatched_types/closure-mismatch.stderr +++ b/tests/ui/mismatched_types/closure-mismatch.stderr @@ -7,28 +7,14 @@ LL | baz(|_| ()); = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/closure-mismatch.rs:8:5 | LL | baz(|_| ()); - | ^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a> Fn(&'a ())` - found trait `Fn(&())` -note: this closure does not fulfill the lifetime requirements - --> $DIR/closure-mismatch.rs:8:9 - | -LL | baz(|_| ()); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/closure-mismatch.rs:5:11 - | -LL | fn baz(_: T) {} - | ^^^ -help: consider specifying the type of the closure parameters - | -LL | baz(|_: &_| ()); - | ~~~~~~~ + = note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/closure-mismatch.rs:11:5 @@ -39,29 +25,14 @@ LL | baz(|x| ()); = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/closure-mismatch.rs:11:5 | LL | baz(|x| ()); - | ^^^^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a> Fn(&'a ())` - found trait `Fn(&())` -note: this closure does not fulfill the lifetime requirements - --> $DIR/closure-mismatch.rs:11:9 - | -LL | baz(|x| ()); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/closure-mismatch.rs:5:11 - | -LL | fn baz(_: T) {} - | ^^^ -help: consider specifying the type of the closure parameters + | ^^^^^^^^^^^ implementation of `Fn` is not general enough | -LL | baz(|x: &_| ()); - | ~~~~~~~ + = note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2` error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/mismatched_types/fn-variance-1.stderr b/tests/ui/mismatched_types/fn-variance-1.stderr index fdb2e6f0097a8..ed450d8d81c86 100644 --- a/tests/ui/mismatched_types/fn-variance-1.stderr +++ b/tests/ui/mismatched_types/fn-variance-1.stderr @@ -10,7 +10,7 @@ LL | apply(&3, takes_mut); | required by a bound introduced by this call | = note: expected function signature `fn(&{integer}) -> _` - found function signature `for<'a> fn(&'a mut isize) -> _` + found function signature `fn(&mut isize) -> _` note: required by a bound in `apply` --> $DIR/fn-variance-1.rs:5:37 | @@ -33,7 +33,7 @@ LL | apply(&mut 3, takes_imm); | required by a bound introduced by this call | = note: expected function signature `fn(&mut {integer}) -> _` - found function signature `for<'a> fn(&'a isize) -> _` + found function signature `fn(&isize) -> _` note: required by a bound in `apply` --> $DIR/fn-variance-1.rs:5:37 | diff --git a/tests/ui/mismatched_types/issue-36053-2.stderr b/tests/ui/mismatched_types/issue-36053-2.stderr index 6d23319ca7e64..ffaa276b62e4b 100644 --- a/tests/ui/mismatched_types/issue-36053-2.stderr +++ b/tests/ui/mismatched_types/issue-36053-2.stderr @@ -7,7 +7,7 @@ LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); | expected due to this | = note: expected closure signature `for<'a> fn(&'a &_) -> _` - found closure signature `for<'a> fn(&'a _) -> _` + found closure signature `fn(&_) -> _` note: required by a bound in `filter` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL help: consider adjusting the signature so it borrows its argument diff --git a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr index 0ed57466e9c58..0b8943898f50b 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr @@ -10,7 +10,7 @@ LL | let _has_inference_vars: Option = Some(0).map(deref_int); | required by a bound introduced by this call | = note: expected function signature `fn({integer}) -> _` - found function signature `for<'a> fn(&'a i32) -> _` + found function signature `fn(&i32) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index 1ac057a5f382f..99c9028ae1efd 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -10,7 +10,7 @@ LL | let _ = produces_string().and_then(takes_str_but_too_many_refs); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a, 'b> fn(&'a &'b str) -> _` + found function signature `fn(&&str) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure @@ -69,7 +69,7 @@ LL | let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); | required by a bound introduced by this call | = note: expected function signature `fn(TypeWithoutDeref) -> _` - found function signature `for<'a, 'b> fn(&'a &'b str) -> _` + found function signature `fn(&&str) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure diff --git a/tests/ui/mismatched_types/suggest-option-asderef.stderr b/tests/ui/mismatched_types/suggest-option-asderef.stderr index 1702a7f1decf6..306c412e9d24d 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef.stderr @@ -10,7 +10,7 @@ LL | let _: Option<()> = produces_string().and_then(takes_str); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a str) -> _` + found function signature `fn(&str) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure @@ -34,7 +34,7 @@ LL | let _: Option> = produces_string().map(takes_str); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a str) -> _` + found function signature `fn(&str) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure @@ -58,7 +58,7 @@ LL | let _: Option> = produces_string().map(takes_str_mut); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a mut str) -> _` + found function signature `fn(&mut str) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure @@ -82,7 +82,7 @@ LL | let _ = produces_string().and_then(generic_ref); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a _) -> _` + found function signature `fn(&_) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure diff --git a/tests/ui/nll/missing-universe-cause-issue-114907.rs b/tests/ui/nll/missing-universe-cause-issue-114907.rs index 9c69c6bdc3670..a3eb5fceea999 100644 --- a/tests/ui/nll/missing-universe-cause-issue-114907.rs +++ b/tests/ui/nll/missing-universe-cause-issue-114907.rs @@ -31,8 +31,8 @@ fn accept(_: C) -> Handshake> { fn main() { let callback = |_| {}; accept(callback); - //~^ ERROR mismatched types - //~| ERROR mismatched types + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR higher-ranked subtype error diff --git a/tests/ui/nll/missing-universe-cause-issue-114907.stderr b/tests/ui/nll/missing-universe-cause-issue-114907.stderr index a616d29c4fea6..26ad1efec0558 100644 --- a/tests/ui/nll/missing-universe-cause-issue-114907.stderr +++ b/tests/ui/nll/missing-universe-cause-issue-114907.stderr @@ -1,25 +1,11 @@ -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/missing-universe-cause-issue-114907.rs:33:5 | LL | accept(callback); - | ^^^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a> FnOnce(&'a ())` - found trait `FnOnce(&())` -note: this closure does not fulfill the lifetime requirements - --> $DIR/missing-universe-cause-issue-114907.rs:32:20 - | -LL | let callback = |_| {}; - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/missing-universe-cause-issue-114907.rs:27:14 - | -LL | fn accept(_: C) -> Handshake> { - | ^^^^^^^^^^^ -help: consider specifying the type of the closure parameters + | ^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | let callback = |_: &_| {}; - | ~~~~~~~ + = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/missing-universe-cause-issue-114907.rs:33:5 @@ -29,6 +15,7 @@ LL | accept(callback); | = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: implementation of `FnOnce` is not general enough --> $DIR/missing-universe-cause-issue-114907.rs:33:5 @@ -40,28 +27,15 @@ LL | accept(callback); = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/missing-universe-cause-issue-114907.rs:33:5 | LL | accept(callback); - | ^^^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a> FnOnce(&'a ())` - found trait `FnOnce(&())` -note: this closure does not fulfill the lifetime requirements - --> $DIR/missing-universe-cause-issue-114907.rs:32:20 - | -LL | let callback = |_| {}; - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/missing-universe-cause-issue-114907.rs:20:21 - | -LL | struct Handshake { - | ^^^^ -help: consider specifying the type of the closure parameters + | ^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | let callback = |_: &_| {}; - | ~~~~~~~ + = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: higher-ranked subtype error --> $DIR/missing-universe-cause-issue-114907.rs:33:21 @@ -79,4 +53,3 @@ LL | accept(callback); error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs index c0e13a5f5f031..5d11941414f43 100644 --- a/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs +++ b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs @@ -26,8 +26,8 @@ static SOME_STRUCT: &SomeStruct = &SomeStruct { foo: &Foo { bools: &[false, true] }, bar: &Bar { bools: &[true, true] }, f: &id, - //~^ ERROR mismatched types - //~| ERROR mismatched types + //~^ ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `Fn` is not general enough //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR implementation of `FnOnce` is not general enough }; diff --git a/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr index 52c700c326e30..5c98a9d4fb4ce 100644 --- a/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr +++ b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr @@ -1,21 +1,20 @@ -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/rfc1623-2.rs:28:8 | LL | f: &id, - | ^^^ one type is more general than the other + | ^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a, 'b> Fn(&'a Foo<'b>)` - found trait `Fn(&Foo<'_>)` + = note: `fn(&'2 Foo<'_>) -> &'2 Foo<'_> {id::<&'2 Foo<'_>>}` must implement `Fn<(&'1 Foo<'b>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 Foo<'_>,)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/rfc1623-2.rs:28:8 | LL | f: &id, - | ^^^ one type is more general than the other + | ^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a, 'b> Fn(&'a Foo<'b>)` - found trait `Fn(&Foo<'_>)` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `Fn<(&'a Foo<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&Foo<'2>,)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/rfc1623-2.rs:28:8 @@ -37,4 +36,3 @@ LL | f: &id, error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr index ee924522564a8..f0acf1dbb963c 100644 --- a/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr +++ b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr @@ -10,7 +10,7 @@ LL | trader.set_closure(closure); | required by a bound introduced by this call | = note: expected closure signature `for<'a, 'b> fn(&'a mut Trader<'b>) -> _` - found closure signature `for<'a> fn(Trader<'a>) -> _` + found closure signature `fn(Trader<'_>) -> _` note: required by a bound in `Trader::<'a>::set_closure` --> $DIR/late-bound-in-borrow-closure-sugg.rs:15:50 | diff --git a/tests/ui/typeck/mismatched-map-under-self.stderr b/tests/ui/typeck/mismatched-map-under-self.stderr index 13678b4b82727..322bf349f92fc 100644 --- a/tests/ui/typeck/mismatched-map-under-self.stderr +++ b/tests/ui/typeck/mismatched-map-under-self.stderr @@ -27,7 +27,7 @@ LL | self.map(Insertable::values).unwrap_or_default() | required by a bound introduced by this call | = note: expected function signature `fn(T) -> _` - found function signature `for<'a> fn(&'a _) -> _` + found function signature `fn(&_) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure From 6729e0188b87dcf8672124195359bb61dc5a2b00 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 29 Feb 2024 10:50:20 +0100 Subject: [PATCH 71/79] one must imagine tidy happy --- src/tools/tidy/src/issues.txt | 1 - ... ambig-hr-projection-issue-93340.next.stderr} | 16 ++++++++-------- ...> ambig-hr-projection-issue-93340.old.stderr} | 4 ++-- ...0-1.rs => ambig-hr-projection-issue-93340.rs} | 0 ...0-2.rs => rigid-hr-projection-issue-93340.rs} | 0 5 files changed, 10 insertions(+), 11 deletions(-) rename tests/ui/generic-associated-types/{issue-93340-1.next.stderr => ambig-hr-projection-issue-93340.next.stderr} (77%) rename tests/ui/generic-associated-types/{issue-93340-1.old.stderr => ambig-hr-projection-issue-93340.old.stderr} (85%) rename tests/ui/generic-associated-types/{issue-93340-1.rs => ambig-hr-projection-issue-93340.rs} (100%) rename tests/ui/generic-associated-types/{issue-93340-2.rs => rigid-hr-projection-issue-93340.rs} (100%) diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 91bbf5041ff58..0ef962c2df870 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1107,7 +1107,6 @@ "ui/generic-associated-types/issue-92954.rs", "ui/generic-associated-types/issue-93141.rs", "ui/generic-associated-types/issue-93262.rs", -"ui/generic-associated-types/issue-93340.rs", "ui/generic-associated-types/issue-93341.rs", "ui/generic-associated-types/issue-93342.rs", "ui/generic-associated-types/issue-93874.rs", diff --git a/tests/ui/generic-associated-types/issue-93340-1.next.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr similarity index 77% rename from tests/ui/generic-associated-types/issue-93340-1.next.stderr rename to tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr index 0a056c9857aaf..6fe128a29f2dc 100644 --- a/tests/ui/generic-associated-types/issue-93340-1.next.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr @@ -1,12 +1,12 @@ error[E0283]: type annotations needed - --> $DIR/issue-93340-1.rs:16:5 + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 | LL | cmp_eq | ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq` | = note: cannot satisfy `_: Scalar` note: required by a bound in `cmp_eq` - --> $DIR/issue-93340-1.rs:9:22 + --> $DIR/ambig-hr-projection-issue-93340.rs:9:22 | LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { | ^^^^^^ required by this bound in `cmp_eq` @@ -16,24 +16,24 @@ LL | cmp_eq:: | +++++++++++ error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` - --> $DIR/issue-93340-1.rs:16:5 + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 | LL | cmp_eq | ^^^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_93340_1`) + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`ambig_hr_projection_issue_93340`) error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` - --> $DIR/issue-93340-1.rs:16:5 + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 | LL | cmp_eq | ^^^^^^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_93340_1`) + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`ambig_hr_projection_issue_93340`) = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0275]: overflow evaluating the requirement `for<'a, 'b> fn(::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::} <: ...` - --> $DIR/issue-93340-1.rs:14:51 + --> $DIR/ambig-hr-projection-issue-93340.rs:14:51 | LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { | ___________________________________________________^ @@ -45,7 +45,7 @@ LL | | LL | | } | |_^ | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_93340_1`) + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`ambig_hr_projection_issue_93340`) error: aborting due to 4 previous errors diff --git a/tests/ui/generic-associated-types/issue-93340-1.old.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr similarity index 85% rename from tests/ui/generic-associated-types/issue-93340-1.old.stderr rename to tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr index 8b6ab16bb0a25..df2ec4ab182aa 100644 --- a/tests/ui/generic-associated-types/issue-93340-1.old.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr @@ -1,12 +1,12 @@ error[E0283]: type annotations needed - --> $DIR/issue-93340-1.rs:16:5 + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 | LL | cmp_eq | ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq` | = note: cannot satisfy `_: Scalar` note: required by a bound in `cmp_eq` - --> $DIR/issue-93340-1.rs:9:22 + --> $DIR/ambig-hr-projection-issue-93340.rs:9:22 | LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { | ^^^^^^ required by this bound in `cmp_eq` diff --git a/tests/ui/generic-associated-types/issue-93340-1.rs b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs similarity index 100% rename from tests/ui/generic-associated-types/issue-93340-1.rs rename to tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs diff --git a/tests/ui/generic-associated-types/issue-93340-2.rs b/tests/ui/generic-associated-types/rigid-hr-projection-issue-93340.rs similarity index 100% rename from tests/ui/generic-associated-types/issue-93340-2.rs rename to tests/ui/generic-associated-types/rigid-hr-projection-issue-93340.rs From 323069fd592c37f6663a41f4304bf99b3ccd3ecd Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 11 Mar 2024 09:44:51 +0100 Subject: [PATCH 72/79] rebase --- compiler/rustc_trait_selection/src/traits/select/mod.rs | 2 +- .../ambig-hr-projection-issue-93340.next.stderr | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index be748de1b17e1..d10fe6a949013 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -43,7 +43,7 @@ use rustc_middle::ty::_match::MatchAgainstFreshVars; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; +use rustc_middle::ty::{self, PolyProjectionPredicate, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::Symbol; diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr index 6fe128a29f2dc..06ffff057f90d 100644 --- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr @@ -20,8 +20,6 @@ error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn( Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 @@ -29,7 +27,6 @@ error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn( fn(::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::} <: ...` @@ -44,8 +41,6 @@ LL | | LL | | LL | | } | |_^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`ambig_hr_projection_issue_93340`) error: aborting due to 4 previous errors From 21904319f82606a67dcecf2984a653950be33949 Mon Sep 17 00:00:00 2001 From: James Farrell Date: Thu, 14 Mar 2024 16:42:15 +0000 Subject: [PATCH 73/79] Update version of cc crate Reason: In order to build the Windows version of the Rust toolchain for the Android platform, the following patch to the cc is crate is required to avoid incorrectly determining that we are building with the Android NDK: https://github.com/rust-lang/cc-rs/commit/57853c4bf8a89a0f4c9137eb367ac580305c6919 This patch is present in version 1.0.80 and newer versions of the cc crate. The rustc source distribution currently has 3 different versions of cc in the vendor directory, only one of which has the necessary fix. We (the Android Rust toolchain) are currently maintaining local patches to upgrade the cc crate dependency versions, which we would like to upstream. --- library/profiler_builtins/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/profiler_builtins/Cargo.toml b/library/profiler_builtins/Cargo.toml index 3371dfa124253..937149f8e86d6 100644 --- a/library/profiler_builtins/Cargo.toml +++ b/library/profiler_builtins/Cargo.toml @@ -13,4 +13,4 @@ core = { path = "../core" } compiler_builtins = { version = "0.1.0", features = ['rustc-dep-of-std'] } [build-dependencies] -cc = "1.0.69" +cc = "1.0.90" From c8f0f17ed2182f6eca05e112e6ec7d2ceeebb8f5 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 14 Mar 2024 17:45:13 +0100 Subject: [PATCH 74/79] add tests --- .../higher-ranked-upcasting-ok.current.stderr | 22 +++++++++++ .../higher-ranked-upcasting-ok.next.stderr | 14 +++++++ .../higher-ranked-upcasting-ok.rs | 19 ++++++++++ .../higher-ranked-upcasting-ub.current.stderr | 22 +++++++++++ .../higher-ranked-upcasting-ub.next.stderr | 14 +++++++ .../higher-ranked-upcasting-ub.rs | 37 +++++++++++++++++++ 6 files changed, 128 insertions(+) create mode 100644 tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.current.stderr create mode 100644 tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.next.stderr create mode 100644 tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs create mode 100644 tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr create mode 100644 tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr create mode 100644 tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.current.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.current.stderr new file mode 100644 index 0000000000000..098ab71e946e9 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.current.stderr @@ -0,0 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ok.rs:17:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` + found existential trait ref `for<'a> Supertrait<'a, 'a>` + +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ok.rs:17:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` + found existential trait ref `for<'a> Supertrait<'a, 'a>` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.next.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.next.stderr new file mode 100644 index 0000000000000..ac516fd697535 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.next.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ok.rs:17:5 + | +LL | fn ok(x: &dyn for<'a, 'b> Subtrait<'a, 'b>) -> &dyn for<'a> Supertrait<'a, 'a> { + | ------------------------------- expected `&dyn for<'a> Supertrait<'a, 'a>` because of return type +LL | x + | ^ expected trait `Supertrait`, found trait `Subtrait` + | + = note: expected reference `&dyn for<'a> Supertrait<'a, 'a>` + found reference `&dyn for<'a, 'b> Subtrait<'a, 'b>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs new file mode 100644 index 0000000000000..0074320317970 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs @@ -0,0 +1,19 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// We should be able to instantiate a binder during trait upcasting. +// This test could be `check-pass`, but we should make sure that we +// do so in both trait solvers. +#![feature(trait_upcasting)] +#![crate_type = "rlib"] +trait Supertrait<'a, 'b> {} + +trait Subtrait<'a, 'b>: Supertrait<'a, 'b> {} + +impl<'a> Supertrait<'a, 'a> for () {} +impl<'a> Subtrait<'a, 'a> for () {} +fn ok(x: &dyn for<'a, 'b> Subtrait<'a, 'b>) -> &dyn for<'a> Supertrait<'a, 'a> { + x //~ ERROR mismatched types + //[current]~^ ERROR mismatched types +} diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr new file mode 100644 index 0000000000000..bac8298326879 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr @@ -0,0 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ub.rs:22:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected existential trait ref `for<'a> Supertrait<'a, 'a>` + found existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` + +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ub.rs:22:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected existential trait ref `for<'a> Supertrait<'a, 'a>` + found existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr new file mode 100644 index 0000000000000..b82f1eef42b5a --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ub.rs:22:5 + | +LL | fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> { + | ----------------------------------- expected `&dyn for<'a, 'b> Supertrait<'a, 'b>` because of return type +LL | x + | ^ expected trait `Supertrait`, found trait `Subtrait` + | + = note: expected reference `&dyn for<'a, 'b> Supertrait<'a, 'b>` + found reference `&dyn for<'a> Subtrait<'a, 'a>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs new file mode 100644 index 0000000000000..2cf6fc75e7779 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs @@ -0,0 +1,37 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// We previously wrongly instantiated binders during trait upcasting, +// allowing the super trait to be more generic than the sub trait. +// This was unsound. +#![feature(trait_upcasting)] +trait Supertrait<'a, 'b> { + fn cast(&self, x: &'a str) -> &'b str; +} + +trait Subtrait<'a, 'b>: Supertrait<'a, 'b> {} + +impl<'a> Supertrait<'a, 'a> for () { + fn cast(&self, x: &'a str) -> &'a str { + x + } +} +impl<'a> Subtrait<'a, 'a> for () {} +fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> { + x //~ ERROR mismatched types + //[current]~^ ERROR mismatched types +} + +fn transmute<'a, 'b>(x: &'a str) -> &'b str { + unsound(&()).cast(x) +} + +fn main() { + let x; + { + let mut temp = String::from("hello there"); + x = transmute(temp.as_str()); + } + println!("{x}"); +} From 6e4cd8b7ccf12d6e4e97a44bbe4fd0093d12ca3d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 14 Mar 2024 13:08:36 -0400 Subject: [PATCH 75/79] Make `SubdiagMessageOp` well-formed --- compiler/rustc_errors/src/diagnostic.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 0c3e7fb75b059..5d345e788e94e 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -189,7 +189,8 @@ where ); } -pub trait SubdiagMessageOp = Fn(&mut Diag<'_, G>, SubdiagMessage) -> SubdiagMessage; +pub trait SubdiagMessageOp = + Fn(&mut Diag<'_, G>, SubdiagMessage) -> SubdiagMessage; /// Trait implemented by lint types. This should not be implemented manually. Instead, use /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. From 442cc762c06887581367f2444352638daa867fb0 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 14 Mar 2024 12:37:40 -0700 Subject: [PATCH 76/79] Use rust-lang/backtrace-rs@6fa4b85 --- library/backtrace | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/backtrace b/library/backtrace index ddf1b89b861d2..6fa4b85b9962c 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit ddf1b89b861d297c6ef3f09b70d853e81ccc85ff +Subproject commit 6fa4b85b9962c3e1be8c2e5cc605cd078134152b From ef5513f278106de9b6c005152fe832be5e197465 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 14 Mar 2024 22:45:57 +0300 Subject: [PATCH 77/79] Fill in HIR hash for associated opaque types --- compiler/rustc_ast_lowering/src/lib.rs | 19 ++-------------- compiler/rustc_middle/src/hir/mod.rs | 27 +++++++++++++++++++++++ compiler/rustc_ty_utils/src/assoc.rs | 12 +++++++--- tests/ui/async-await/in-trait/hir-hash.rs | 11 +++++++++ 4 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 tests/ui/async-await/in-trait/hir-hash.rs diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 94e1e06a95453..9f63adb1e29ec 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -642,23 +642,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let bodies = SortedMap::from_presorted_elements(bodies); // Don't hash unless necessary, because it's expensive. - let (opt_hash_including_bodies, attrs_hash) = if self.tcx.needs_crate_hash() { - self.tcx.with_stable_hashing_context(|mut hcx| { - let mut stable_hasher = StableHasher::new(); - node.hash_stable(&mut hcx, &mut stable_hasher); - // Bodies are stored out of line, so we need to pull them explicitly in the hash. - bodies.hash_stable(&mut hcx, &mut stable_hasher); - let h1 = stable_hasher.finish(); - - let mut stable_hasher = StableHasher::new(); - attrs.hash_stable(&mut hcx, &mut stable_hasher); - let h2 = stable_hasher.finish(); - - (Some(h1), Some(h2)) - }) - } else { - (None, None) - }; + let (opt_hash_including_bodies, attrs_hash) = + self.tcx.hash_owner_nodes(node, &bodies, &attrs); let num_nodes = self.item_local_id_counter.as_usize(); let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes); let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies }; diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 61bdb5d4bb705..f9fa8ac2f7aa5 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -8,6 +8,9 @@ pub mod place; use crate::query::Providers; use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::sorted_map::SortedMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{try_par_for_each_in, DynSend, DynSync}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; @@ -121,6 +124,30 @@ impl<'tcx> TyCtxt<'tcx> { self.opt_parent(def_id.into()) .is_some_and(|parent| matches!(self.def_kind(parent), DefKind::ForeignMod)) } + + pub fn hash_owner_nodes( + self, + node: OwnerNode<'_>, + bodies: &SortedMap>, + attrs: &SortedMap, + ) -> (Option, Option) { + if self.needs_crate_hash() { + self.with_stable_hashing_context(|mut hcx| { + let mut stable_hasher = StableHasher::new(); + node.hash_stable(&mut hcx, &mut stable_hasher); + // Bodies are stored out of line, so we need to pull them explicitly in the hash. + bodies.hash_stable(&mut hcx, &mut stable_hasher); + let h1 = stable_hasher.finish(); + + let mut stable_hasher = StableHasher::new(); + attrs.hash_stable(&mut hcx, &mut stable_hasher); + let h2 = stable_hasher.finish(); + (Some(h1), Some(h2)) + }) + } else { + (None, None) + } + } } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 3f628092190b5..4bcbf1c037499 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -240,8 +240,14 @@ fn associated_types_for_impl_traits_in_associated_fn( fn feed_hir(feed: &TyCtxtFeed<'_, LocalDefId>) { feed.local_def_id_to_hir_id(HirId::make_owner(feed.def_id())); + + let node = hir::OwnerNode::AssocOpaqueTy(&hir::AssocOpaqueTy {}); + let bodies = Default::default(); + let attrs = hir::AttributeMap::EMPTY; + + let (opt_hash_including_bodies, _) = feed.tcx.hash_owner_nodes(node, &bodies, &attrs.map); feed.opt_hir_owner_nodes(Some(feed.tcx.arena.alloc(hir::OwnerNodes { - opt_hash_including_bodies: None, + opt_hash_including_bodies, nodes: IndexVec::from_elem_n( hir::ParentedNode { parent: hir::ItemLocalId::INVALID, @@ -249,9 +255,9 @@ fn feed_hir(feed: &TyCtxtFeed<'_, LocalDefId>) { }, 1, ), - bodies: Default::default(), + bodies, }))); - feed.feed_owner_id().hir_attrs(hir::AttributeMap::EMPTY); + feed.feed_owner_id().hir_attrs(attrs); } /// Given an `opaque_ty_def_id` corresponding to an `impl Trait` in an associated diff --git a/tests/ui/async-await/in-trait/hir-hash.rs b/tests/ui/async-await/in-trait/hir-hash.rs new file mode 100644 index 0000000000000..8324fec8282f1 --- /dev/null +++ b/tests/ui/async-await/in-trait/hir-hash.rs @@ -0,0 +1,11 @@ +// Issue #122508 + +//@ check-pass +//@ incremental +//@ edition:2021 + +trait MyTrait { + async fn bar(&self) -> i32; +} + +fn main() {} From 3cc4059a6e8a6aebcd020b0d3a0e2eb33d926c2c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 15 Mar 2024 07:55:46 +0100 Subject: [PATCH 78/79] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 721d6df0f1104..e32968d8178e8 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -5ac0b2d0219de2fd6fef86c69ef0cfa1e6c36f3b +ee03c286cfdca26fa5b2a4ee40957625d2c826ff From 32c734b73cf6eada0b330337e24fee2c6039cc84 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 15 Mar 2024 08:09:46 +0100 Subject: [PATCH 79/79] fmt --- src/tools/miri/src/diagnostics.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 6e612ea34a70f..03428b081c569 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -527,8 +527,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) { use NonHaltingDiagnostic::*; - let stacktrace = - Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack()); + let stacktrace = Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack()); let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self); let (title, diag_level) = match &e {