Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2531,7 +2531,38 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
}
}

/// `associated_types_for_impl_traits_in_trait_or_impl` creates new `DefId`-s inside the query. We
/// must make sure this is done in a deterministic order (not in parallel).
fn assign_anon_assoc_item_def_ids(tcx: TyCtxt<'_>) {
let items = tcx.hir_crate_items(());
for def_id in items.free_items().map(|item| item.owner_id.def_id) {
match tcx.def_kind(def_id) {
DefKind::Trait | DefKind::Impl { .. } => {
tcx.ensure_ok().associated_types_for_impl_traits_in_trait_or_impl(def_id);
}
_ => (),
}
}
}

/// `resolve_bound_vars` creates new `DefId`-s inside the query (in `remap_opaque_captures`). We
/// must make sure this is done in a deterministic order (not in parallel).
fn remap_opaque_captures(tcx: TyCtxt<'_>) {
let items = tcx.hir_crate_items(());
for def_id in items.opaques() {
let opaque = tcx.hir_expect_opaque_ty(def_id);
let origin_id = match opaque.origin {
rustc_hir::OpaqueTyOrigin::TyAlias { parent, .. }
| rustc_hir::OpaqueTyOrigin::AsyncFn { parent, .. }
| rustc_hir::OpaqueTyOrigin::FnReturn { parent, .. } => parent,
};
tcx.ensure_ok().resolve_bound_vars(rustc_hir::OwnerId { def_id: origin_id });
}
}

pub(super) fn check_type_wf(tcx: TyCtxt<'_>, (): ()) -> Result<(), ErrorGuaranteed> {
assign_anon_assoc_item_def_ids(tcx);
remap_opaque_captures(tcx);

@petrochenkov petrochenkov Sep 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These calls can be put under if tcx.sess.opts.jobs.frontend.is_some() to avoid regressing single-threaded performance.

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do assign_anon_assoc_item_def_ids/remap_opaque_captures create the def ids in the same order as single-threaded check_type_wf?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but likely not. The regression seems to come from calling resolve_bound_vars for everything.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified remap_opaque_captures to only run resolve_bound_vars on the parents of opaques. It solved the problem locally on the biggest regression, typenum

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but likely not.

Ideally, changing -j N to -j M should not change the produced binaries (including when N or M is 1).

let items = tcx.hir_crate_items(());
let res =
items
Expand Down
62 changes: 62 additions & 0 deletions tests/run-make/parallel-reproducible-async-fn/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//@ needs-target-std
//@ ignore-cross-compile
//@ ignore-windows-gnu
// GNU Linker for Windows is non-deterministic. (from `reproducible-build-2` test in this suite)

use std::rc::Rc;

use run_make_support::{rfs, run_in_tmpdir, rustc};

/// Test that parallel compiler produces identical binaries.
fn main() {
const FILE_NAME: &str = "rpit-issue-162202";
let rmeta_name = format!("{FILE_NAME}.rmeta");

let mut reference = None;
let mut reference_stderr = None;

for _ in 0..10 {
// Tmp dir as previous runs affect output binary on windows.
run_in_tmpdir(|| {
let mut rustc = rustc();
rustc
.input(format!("{FILE_NAME}.rs"))
.arg("--edition=2024")
.arg("-Zremap-cwd-prefix=reproducible_dir")
.arg("-Ccodegen-units=1")
.arg("-Zthreads=2")
.arg("--crate-type=lib")
.emit("metadata")
.output(&rmeta_name);

let current_stderr = rustc.run().stderr_utf8();

let current = Rc::new(rfs::read(&rmeta_name));
reference.get_or_insert(Rc::clone(&current));
let reference_stderr = reference_stderr.get_or_insert_with(|| current_stderr.clone());

if Some(current.clone()) != reference {
let reference_bytes = reference.as_ref().unwrap();
let (pos, (left_byte, right_byte)) = current
.iter()
.zip(reference_bytes.iter())
.enumerate()
.find(|(_, (c, r))| c != r)
.unwrap();
let range_start = pos.saturating_sub(1);
let range_end = (pos + 3).min(current.len()).min(reference_bytes.len());
panic!(
"left: {current:x?}\nright: {reference:x?}\n \
differs at byte {pos}: left = {left_byte:#x}, right = {right_byte:#x}\n\
left range [{range_start}..{range_end}]: {:x?}\n\
right range [{range_start}..{range_end}]: {:x?}\n\
left stderr:\n{current_stderr}\n\
right stderr:\n{reference_stderr}",
&current[range_start..range_end],
&reference_bytes[range_start..range_end],
)
}
assert_eq!(Some(current), reference);
});
}
}
22 changes: 22 additions & 0 deletions tests/run-make/parallel-reproducible-async-fn/rpit-issue-162202.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
trait Foo {
fn test() -> impl IntoIterator<Item = ()> + Send;
}

struct A;
impl Foo for A {
fn test() -> impl IntoIterator<Item = ()> + Send {
[]
}
}

struct B;
impl Foo for B {
fn test() -> impl IntoIterator<Item = ()> + Send {
[]
}
}

async fn test1(_: &'_ u8) {}
async fn test2<'s>(_: &'s u8) {}

fn main() {}
Loading