Skip to content

Conversation

@rikkimax
Copy link
Contributor

@rikkimax rikkimax commented Oct 28, 2025

Approved work: https://forum.dlang.org/post/[email protected]

Currently turned on to see what the CI does.

Will request Walter to review once I'm happy with CI.

Heavily inspired by OpenD with thanks to @adamdruppe.

EDIT: Special thanks to @limepoutine for helping to get both function pointers and delegate function pointers to be null checked!

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @rikkimax! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub run digger -- build "master + dmd#22040"

@rikkimax rikkimax force-pushed the nullderef-barrier branch 5 times, most recently from 1ca48e7 to c0d2c45 Compare October 28, 2025 10:23
@rikkimax
Copy link
Contributor Author

Fun fact, lazy parameters can have a null object. Oh well we just can't check for null on the object of a delegate prior to a call.

@rikkimax rikkimax force-pushed the nullderef-barrier branch 13 times, most recently from 86f1e3b to 05957b2 Compare October 28, 2025 13:46
@nordlow
Copy link
Contributor

nordlow commented Oct 28, 2025

So a NullPointerError is unrecoverable, right?

@rikkimax
Copy link
Contributor Author

So I take it a NullPointerError unrecoverable, right?

It is an Error. Something has to die in response, like all Error's.

@nordlow
Copy link
Contributor

nordlow commented Oct 28, 2025

Is the plan to have -check=nullderef=on be on my default or optionally in debug mode or something else?

@adamdruppe
Copy link
Contributor

There's no technical reason why you can't recover from a NullPointerError in general, though you may choose not to as it can indicate you don't understand program state.

@rikkimax
Copy link
Contributor Author

Will using -check=nullderef=on affect performance?

Possibly.

I intend to wire up the fast DFA engine to elide the checks. But can't do it until what does exist is proven to work.

@adamdruppe
Copy link
Contributor

The performance impact is very small. The cpu's branch predictor handles it well and ldc's code generator is decent at removing dead checks and arranging the code blocks in a cache-friendly manner. (dmd's isn't so good but ldc is known to optimize better than dmd in many ways)

@nordlow
Copy link
Contributor

nordlow commented Oct 29, 2025

So a single unittest in libdparse at

https://buildkite.com/dlang/dmd/builds/43787/steps/canvas?sid=019a2b1c-9d02-4993-b9df-dfb58a18783d

fails buildkite/dmd.

The libdparse/test/fail_files/killer2.d contains

unittest
{
version (53056)[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
union {}
Klse
gosh ÿ� 'A+ SPL : tape];
		}
	}
}

. Could the CI failure be caused by the compiler existing with error code other than what the tests expects? I also wonder how these seemingly random test cases where deduced...

@rikkimax
Copy link
Contributor Author

I'm aware, but the problem will be in the compiled libdparse, and without compiling it locally I won't be figuring out which emitted hook did it.

As far as I know this could be related to XMM intrinsics, and since those are more of a known problem they're up first.

@rikkimax rikkimax force-pushed the nullderef-barrier branch 5 times, most recently from 2930e3d to f241687 Compare December 9, 2025 17:52
@rikkimax
Copy link
Contributor Author

rikkimax commented Dec 9, 2025

I just spent a good 8 hours debugging automem, thinking it wasn't supposed to fail.

https://github.com/atilaneves/automem/blob/master/source/automem/ref_counted.d#L181

I wasn't even looking in the right library, thought it was Phobos's one lol.

@rikkimax rikkimax force-pushed the nullderef-barrier branch 2 times, most recently from 32c1384 to 5ee989a Compare December 9, 2025 18:59
@rikkimax
Copy link
Contributor Author

rikkimax commented Dec 9, 2025

For the record, here is a known good implementation for delegate function pointer check.
2930e3d#diff-54719c55eec4a6cfe3b7b7f4b5289d0b5a0b9d41e8589a023bba4d68921f8be0

The reason I'm not using that is because I think it can be improved quite significantly.

@rikkimax rikkimax force-pushed the nullderef-barrier branch 3 times, most recently from eb4a825 to 57ed5c1 Compare December 9, 2025 19:48
@limepoutine
Copy link
Contributor

limepoutine commented Dec 10, 2025

I just spent a good 8 hours debugging automem, thinking it wasn't supposed to fail.

https://github.com/atilaneves/automem/blob/master/source/automem/ref_counted.d#L181

I wasn't even looking in the right library, thought it was Phobos's one lol.

The test tests exactly that AssertError, i.e. that thrown in _get().

@rikkimax
Copy link
Contributor Author

I just spent a good 8 hours debugging automem, thinking it wasn't supposed to fail.
https://github.com/atilaneves/automem/blob/master/source/automem/ref_counted.d#L181
I wasn't even looking in the right library, thought it was Phobos's one lol.

The test tests exactly that AssertError, i.e. that thrown in _get().

Let's just say I was barely seeing water at 3am, that's how deep I was into CI failures problems I was trying to fix!

However, on the plus side, I like the solution a lot better than we came up with previously.

@limepoutine
Copy link
Contributor

This is much cleaner than the previous one. Thank you for the work.

@rikkimax
Copy link
Contributor Author

CI has done the correct, thing.

Anything else you need @WalterBright before I turn it off and take it out of draft?

@@ -0,0 +1,8 @@
On null dereference check is now available
Copy link
Member

Choose a reason for hiding this comment

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

An optional check for a null dereference is added.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -0,0 +1,8 @@
On null dereference check is now available

A new check is implemented for when a null dereference occurs.
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand that sentence. Perhaps: "code is inserted to check a reference for null before it is dereferenced."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

This can be enabled by using ``-check=nullderef=on``
What happens may be customized by the ``-checkaction`` switch and by setting a new handler in ``core.exception``.

Due to issues in dmd's backend, not all pointer dereferences are guaranteed to get a check.
Copy link
Member

Choose a reason for hiding this comment

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

What does that mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Disabling of intrinsics and their arguments.

I don't particularly want to explain this in a changelog as it won't impact everyone, but it does need acknowledgment that dmd specifically can't check all pointer dereferences.

I can disable the intrinsic check in a follow-up PR for you to look into.

A new check is implemented for when a null dereference occurs.

This can be enabled by using ``-check=nullderef=on``
What happens may be customized by the ``-checkaction`` switch and by setting a new handler in ``core.exception``.
Copy link
Member

Choose a reason for hiding this comment

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

No mention of what the default behavior is.

Copy link
Member

Choose a reason for hiding this comment

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

Could add a brief discussion of why one would want this feature.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

/// What should happen when a dereference on null happens
enum CHECKACTIONDEREF : ubyte
{
D, /// call D hook function
Copy link
Member

Choose a reason for hiding this comment

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

What is the "D hook function"?

{
D, /// call D hook function
Assert, /// lower to an assert
Ignore /// ignore
Copy link
Member

Choose a reason for hiding this comment

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

What happens if one turns on checking and then ignores it? What is the purpose of that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Dead code, removed.

@rikkimax
Copy link
Contributor Author

I've turned this off now, will need confirmation that @WalterBright doesn't want any more changes.

@rikkimax rikkimax marked this pull request as ready for review December 11, 2025 13:46
@rikkimax rikkimax requested a review from ibuclaw as a code owner December 11, 2025 13:46
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.

7 participants