Skip to content

Swaps raw pointer types with NonNull for better safety and niches#687

Open
ErisianArchitect wants to merge 5 commits intoTheDan64:masterfrom
ErisianArchitect:master
Open

Swaps raw pointer types with NonNull for better safety and niches#687
ErisianArchitect wants to merge 5 commits intoTheDan64:masterfrom
ErisianArchitect:master

Conversation

@ErisianArchitect
Copy link
Copy Markdown
Contributor

Description

Many of the types used LLVM*Ref types, which are type aliases for *mut LLVM*. These pointers are null-checked before creating new instances of these types, so I thought it would make sense to use NonNull instead so that there would be better safety guarantees, and it would enable these types to have at least one niche, allowing for this condition: size_of::<T>() == size_of::<Option<T>>() && align_of::<T>() == align_of::<Option<T>>().

Related Issue

Closes #686

How This Has Been Tested

I finally figured out clippy, so I have a bash function that I run that runs clippy, runs all the tests, then runs cargo fmt. So all checks should hopefully pass on the first try. Fingers crossed.

It's quite a big update. It was extremely repetitive and tedious, but I think I got everything.

Option<Breaking Changes>

I'm pretty sure there are no breaking changes.

Checklist

@ErisianArchitect
Copy link
Copy Markdown
Contributor Author

Third times the charm, I guess.

@ErisianArchitect
Copy link
Copy Markdown
Contributor Author

Oh, I forgot to mention, I also added a couple functions to support.

const_assert and assert_niche.

assert_niche is used to assert that types have a niche (same size and alignment as Option<T>).

const_assert is just used by assert_niche, but it could be used anywhere that a compile time assertions needs to be done.

#[repr(transparent)]
#[derive(Debug)]
pub struct OperandBundle<'ctx> {
bundle: Cell<LLVMOperandBundleRef>,
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.

As far as I could tell, bundle did not need to be a Cell, so I changed that too.

Comment thread src/targets.rs
unsafe { *self.0.get_or_insert_with(|| LLVMCreateTargetMachineOptions()) }
unsafe {
self.0
.get_or_insert_with(|| NonNull::new_unchecked(LLVMCreateTargetMachineOptions()))
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.

This is honestly a little bit weird, but it works.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Do you think The struct should just be initialized with LLVMCreateTargetMachineOptions? Or does it being Option make sense?

Copy link
Copy Markdown
Contributor Author

@ErisianArchitect ErisianArchitect left a comment

Choose a reason for hiding this comment

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

I don't know if it counts for much, but I read through the entire codebase at least twice while working on this PR, and have now read through all of my changes.

@ErisianArchitect
Copy link
Copy Markdown
Contributor Author

ErisianArchitect commented Apr 18, 2026

Oh, and one last note: I didn't apply this optimization to passes.rs. It's not that I couldn't have for some reason, it's just that I'd already read through all the code twice by the time I got to that, and I didn't want to go back and read it again (because of all the conditionally compiled blocks that don't show errors in my environment). I did the best that I could. Considering all the checks passed, I'm hoping that I didn't make any mistakes. I hope this optimization is worth the time I just spent on this, because it was a lot more work than I thought it was going to be.

@TheDan64
Copy link
Copy Markdown
Owner

Looks cool, it'll take me a while to review it all

Comment thread src/support/mod.rs
pub(crate) enum LLVMStringOrRaw {
Owned(LLVMString),
Borrowed(*const c_char),
Borrowed(NonNull<c_char>),
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

If LLVMString and NonNull are both niche, doesn't that mean LLVMStringOrRaw can be niche as well? Or does the enum miss out on it?

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.

No, unfortunately it misses out on the niche since it needs an extra value for the discriminant. It can't say this variant and this value or that variant and that value in a flattened way. But LLVMStringOrRaw will have plenty of niches anyway, 2^63, I believe.

Comment thread src/support/mod.rs
#[inline(always)]
pub(crate) const fn const_assert(assertion: bool) {
if !assertion {
panic!("Assertion Failed");
Copy link
Copy Markdown
Owner

@TheDan64 TheDan64 Apr 20, 2026

Choose a reason for hiding this comment

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

Is it possible to be able to take a static string? Would be nice if the panic had more specific details

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.

Unfortunately, no, not at compile time. panic expects a format argument, and format args, but it can't perform formatting at compile time.

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.

The best recommendation is to include a comment with the assertion. It's marked with #[track_caller], so the error will appear at the location of the assertion.

@ErisianArchitect
Copy link
Copy Markdown
Contributor Author

Looks cool, it'll take me a while to review it all

Yeah, sorry about that, haha. I really did not expect this change to affect this many lines.

Comment thread src/data_layout.rs
LLVMStringOrRaw::Owned(ref llvm_string) => llvm_string.ptr,
LLVMStringOrRaw::Borrowed(ptr) => ptr,
LLVMStringOrRaw::Owned(ref llvm_string) => llvm_string.ptr.as_ptr(),
LLVMStringOrRaw::Borrowed(ptr) => ptr.as_ptr().cast_const(),
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

"cast_const" gives me C++ ptsd 😂

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Swapping pointer values that should never be null with NonNull variants.

2 participants