Skip to content

Conversation

@cjgillot
Copy link
Contributor

@cjgillot cjgillot commented Oct 26, 2025

This PR changes the way we compute the value of the offset_of! macro in MIR. The current implementation uses a dedicated MIR rvalue.

This PR proposes to replace it by an inline constant which sums calls to a new intrinsic offset_of(variant index, field index). The desugaring is done at THIR building time, easier that doing it on MIR.

The new intrinsic is only meant to be used by const-eval. LLVM codegen will refuse to generate code for it.

We replace:

a = offset_of!(T, Variant1.Field1.Variant2.Field2);

By:

a = const {constant#n};

{constant#n}: usize = {
    _1 = offset_of::<T>(index of Variant1, index of Field1);
    _2 = offset_of::<U>(index of Variant2, index of Field2); // Where T::Variant1::Field1 has type U
    _0 = _1 + _2
}

The second commit modifies intrinsic const checking to take allow_internal_unstable into account. The new intrinsic should only be called from stable offset_of! macro. The intrinsic itself is unstable, const-unstable, but rustc_intrinsic_const_stable_indirect.

Fixes #123959
Fixes #125680
Fixes #129425
Fixes #136175

r? @ghost

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Oct 26, 2025
@rust-log-analyzer

This comment has been minimized.

@cjgillot
Copy link
Contributor Author

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rust-bors

This comment has been minimized.

rust-bors bot added a commit that referenced this pull request Oct 27, 2025
Replace OffsetOf by an actual sum of calls to intrinsic.
@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Oct 27, 2025
@rust-bors
Copy link

rust-bors bot commented Oct 27, 2025

☀️ Try build successful (CI)
Build commit: 7721497 (772149783c4c312fd88b7dfaa68a045b14db3280, parent: f37aa9955f03bb1bc6fe08670cb1ecae534b5815)

@rust-timer

This comment has been minimized.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (7721497): comparison URL.

Overall result: ❌✅ regressions and improvements - no action needed

Benchmarking this pull request means it may be perf-sensitive – we'll automatically label it not fit for rolling up. You can override this, but we strongly advise not to, due to possible changes in compiler perf.

@bors rollup=never
@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
0.1% [0.0%, 0.1%] 3
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.1% [-0.1%, -0.1%] 3
All ❌✅ (primary) - - 0

Max RSS (memory usage)

Results (secondary -1.9%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
2.6% [2.1%, 3.2%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-3.7% [-4.9%, -1.5%] 5
All ❌✅ (primary) - - 0

Cycles

Results (secondary -0.8%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
2.7% [1.6%, 5.0%] 3
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-6.1% [-7.9%, -4.2%] 2
All ❌✅ (primary) - - 0

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 473.876s -> 473.729s (-0.03%)
Artifact size: 390.50 MiB -> 390.46 MiB (-0.01%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Oct 27, 2025
@bors
Copy link
Collaborator

bors commented Oct 31, 2025

☔ The latest upstream changes (presumably #148324) made this pull request unmergeable. Please resolve the merge conflicts.

@cjgillot cjgillot marked this pull request as ready for review November 1, 2025 23:47
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Nov 1, 2025
@rustbot
Copy link
Collaborator

rustbot commented Nov 1, 2025

Some changes occurred in match checking

cc @Nadrieril

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

⚠️ #[rustc_intrinsic_const_stable_indirect] controls whether intrinsics can be exposed to stable const
code; adding it needs t-lang approval.

cc @rust-lang/wg-const-eval

Some changes occurred to constck

cc @fee1-dead

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

This PR changes rustc_public

cc @oli-obk, @celinval, @ouz-a

Some changes occurred in rustc_ty_utils::consts.rs

cc @BoxyUwU

Some changes occurred to the intrinsics. Make sure the CTFE / Miri interpreter
gets adapted for the changes, if necessary.

cc @rust-lang/miri, @RalfJung, @oli-obk, @lcnr

This PR changes MIR

cc @oli-obk, @RalfJung, @JakobDegen, @vakaras

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

Some changes occurred to the CTFE / Miri interpreter

cc @rust-lang/miri

Some changes occurred in compiler/rustc_codegen_ssa

cc @WaffleLapkin

This PR changes a file inside tests/crashes. If a crash was fixed, please move into the corresponding ui subdir and add 'Fixes #' to the PR description to autoclose the issue upon merge.

@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Nov 1, 2025
@cjgillot cjgillot added the I-lang-nominated Nominated for discussion during a lang team meeting. label Nov 2, 2025
@oli-obk oli-obk self-assigned this Nov 2, 2025
@BoxyUwU
Copy link
Member

BoxyUwU commented Nov 2, 2025

What's the justification/benefit for doing this, and what user facing impact does it have (i.e. why does it need a lang nomination)?

@cjgillot
Copy link
Contributor Author

cjgillot commented Nov 2, 2025

My justification is simplifying MIR. This feature was implemented as a specific MIR statement, but does not need to, an intrinsic is sufficient.

There should be no user-facing change because of the offset_of manipulation. Maybe some diagnostics, but not more.

However, there are 2 user-facing changes in this PR that t-lang may want to know:

  • const-stability of intrinsics now takes allow_internal_unstable into account;
  • we get an additional intrinsic with rustc_intrinsic_const_stable_indirect.

@WaffleLapkin

This comment was marked as resolved.

@cjgillot

This comment was marked as resolved.

@traviscross traviscross added the T-lang Relevant to the language team label Nov 5, 2025
@rust-rfcbot
Copy link
Collaborator

🔔 This is now entering its final comment period, as per the review above. 🔔

Copy link
Member

@RalfJung RalfJung left a comment

Choose a reason for hiding this comment

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

rustc_const_eval changes LGTM.

View changes since this review

let tp_ty = instance.args.type_at(0);

let u32_layout = self.layout_of(self.tcx.types.u32)?;
let variant = self.read_scalar(&args[0])?.to_bits(u32_layout.size)? as u32;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let variant = self.read_scalar(&args[0])?.to_bits(u32_layout.size)? as u32;
let variant = self.read_scalar(&args[0])?.to_u32()?;


let u32_layout = self.layout_of(self.tcx.types.u32)?;
let variant = self.read_scalar(&args[0])?.to_bits(u32_layout.size)? as u32;
let field = self.read_scalar(&args[1])?.to_bits(u32_layout.size)? as usize;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
let field = self.read_scalar(&args[1])?.to_bits(u32_layout.size)? as usize;
let field = self.read_scalar(&args[1])?.to_u32()? as usize;

/// Therefore, implementations must not require the user to uphold
/// any safety invariants.
///
/// The stabilized version of this intrinsic is [`core::mem::offset_of`].
Copy link
Member

Choose a reason for hiding this comment

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

This can also only be called at compile time, right? Please add that note to the comment.

Also, would be good to explain why this is a lang item, since that is very unusual for intrinsics.

@bors
Copy link
Collaborator

bors commented Nov 9, 2025

☔ The latest upstream changes (presumably #148721) made this pull request unmergeable. Please resolve the merge conflicts.

@rustbot

This comment has been minimized.

@bors
Copy link
Collaborator

bors commented Nov 11, 2025

☔ The latest upstream changes (presumably #148658) made this pull request unmergeable. Please resolve the merge conflicts.

@rust-rfcbot rust-rfcbot added finished-final-comment-period The final comment period is finished for this PR / Issue. and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. labels Nov 15, 2025
@rust-rfcbot
Copy link
Collaborator

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

This will be merged soon.

// CHECK: concrete::[[const_z0]]: usize
// CHECK: [[z:_.*]] = offset_of::<Alpha>(const 0_u32, const 2_u32)
// CHECK: [[z0:_.*]] = offset_of::<Beta>(const 0_u32, const 0_u32)
// CHECK: [[sum:_.*]] = AddWithOverflow(copy [[z]], copy [[z0]]);
Copy link
Member

Choose a reason for hiding this comment

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

pondering: Can this ever overflow? Wouldn't the type have to be malformed for it to happen? Since we're only ever running it in CTFE, could it be AddUnchecked instead, and if it ever does overflow it'll get trapped by the UB-in-CTFE checks?

(Aside: appreciate the test here -- I came looking for it as soon as I was reading the code in mir_build.)

Copy link
Member

Choose a reason for hiding this comment

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

Actually, I'm kinda confused how it's AddWithOverflow here -- at https://github.com/rust-lang/rust/pull/148151/files#diff-ad5988b23007e07f23d5459907b7aa75c9dc6ae82623be8cd4bc6acc58cb3397R863 it's BinOp::Add, not BinOp::AddWithOverflow.

Is there a TerminatorKind::Assert in here too somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It cannot possibly overflow, but as I made the transform happen on THIR, removing the overflow checks would only make stuff more complex.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a THIR-print test to show what the generated THIR looks like.

#[rustc_intrinsic_const_stable_indirect]
#[rustc_intrinsic]
#[lang = "offset_of"]
pub const fn offset_of<T: PointeeSized>(variant: u32, field: u32) -> usize;
Copy link
Member

Choose a reason for hiding this comment

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

Musing: Is there ever a need to pass values into this? Why is it phrased this way as opposed to, say

Suggested change
pub const fn offset_of<T: PointeeSized>(variant: u32, field: u32) -> usize;
pub const fn offset_of<T: PointeeSized, const Variant: u32, const Field: u32>() -> usize;

(Not that the way it is is bad -- I think it's fine -- just came to mind as I was reading through stuff.)

Copy link
Member

@scottmcm scottmcm left a comment

Choose a reason for hiding this comment

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

r=me with the trivial comment fix. I left a bunch of other things on the way by that you can think about as well, but I don't think any of them are critical.

View changes since this review

@rustbot

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Collaborator

bors commented Nov 16, 2025

☔ The latest upstream changes (presumably #148957) made this pull request unmergeable. Please resolve the merge conflicts.

@rustbot
Copy link
Collaborator

rustbot commented Nov 16, 2025

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this PR / Issue. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team T-libs Relevant to the library team, which will review and decide on the PR/issue. to-announce Announce this issue on triage meeting

Projects

None yet