Should I worry about this hashOf warning ?

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Jun 26 03:55:03 UTC 2025


On Wednesday, June 25, 2025 9:38:55 AM Mountain Daylight Time axricard via Digitalmars-d-learn wrote:
> When trying to use Nullable with a struct containing an
> overlapping pointer:
>
> ```D
> import std;
>
> struct S
> {
>      union
>      {
>       long foo;
>           int[] bar;
>      }
> }
>
> void main()
> {
>      Nullable!S s;
> }
> ```
>
> I get this warning:
>
>   ```
> /dlang/dmd/linux/bin64/../../src/phobos/std/typecons.d(3820):
> Deprecation: `@safe` function `toHash` calling `hashOf`
>              return _isNull ? 0 : .hashOf(_value.payload);
>                                          ^
> /dlang/dmd/linux/bin64/../../src/druntime/import/core/internal/hash.d-mixin-551(664):        and accessing overlapped field `S.bar` with pointers makes it fail to infer `@safe`
> ```
>
> The [spec](https://dlang.org/spec/function.html#safe-functions)
> is states that a @safe functions "cannot access union fields that
> have pointers or references overlapping with other types", and
> this toHash in std.typecons is @safe.
>
> So I wonder, why is it just a warning and not a compile error
> (the code compiles and runs normally) ? Is there any risk at
> ignoring this warning ?
>
> And if there is a risk, is there a workaround or do I have to
> drop the Nullable struct ?

Strictly speaking, it's not a warning. It's a deprecation message. I'd have
to dig through the changelog to see where this particular decprecation is
mentioned to know exactly what's going on with it, but almost certainly what
happened is that the code was treated as @safe previously when it shouldn't
have been. Someone then figured out that it wasn't actually memory safe, so
it needs to be treated as @system, but making that change to the compiler
would break code. So, instead of simply fixing the issue and making it
@system, it was first made a deprecation message in order to get folks to
fix their code. And then later, the compiler will be change do treat it as
@system, breaking any code marked with @safe which hadn't been fixed yet.

In this case, the bug is in Nullable's toString in that it's calling hashOf
and marked as @safe when using hashOf in this situation isn't @safe. And
since Nullable is templated, this issue is only visible when Nullable is
instantiated with a type like the one you have here, so Nullable's toHash
needs to be fixed to infer @safe instead of being explicitly marked with it.
I've opened a bug report for the issue:

But looking into this deeper, it looks like Nullable's toHash is just plain
broken with any type whose toHash isn't @safe const nothrow (or some
variation on that which can be called from code which is @safe const
nothrow). Nullable's toHash is actually giving the wrong hash with code in
such a situation, because it's bypassing the contained type's toHash. I'm
pretty sure that this was done to try to make Nullable's toHash work with
types whose hash isn't const, @safe, and nothrow, but it introduced
incorrect behavior when what really needed to happen was make toHash infer
its attributes and then make the code that required those attributes but
couldn't infer them break, since that code is broken anyway. I've reported
the issue:

https://github.com/dlang/phobos/issues/10809

As for your situation, if Nullable's toHash were correct, then your
Nullable!S would be @system, so then you'd need to just either live with the
deprecation message or fix your type to have a toHash which wasn't @system.

And what you really want here is to provide a toHash and opEquals which use
whichever field is the valid one rather than using the entire union. That
way, your code can be @safe. I don't recall at the moment whether your code
as-is is hashing the entire union or just the first member, but it's highly
unlikely that it's working in a way that you actually want regardless of
@safe.

And in case you didn't know, with types in general, if you provide toHash,
you need to provide an opEquals which is consistent with it (otherwise
values which are considered equal might end up with different hashes, which
is a bug), and if you provide an opEquals, you either can't use toHash with
that type anywhere, or you need to provide a toHash which is consistent with
your opEquals.

However, since Nullable's toHash is not currently correct, to get correct
behavior, you not only need to provide an opEquals and toHash, but you need
the toHash to be marked as @safe (or @trusted), const, and nothrow;
otherwise, Nullable's toHash will use its broken workaround.

What will happen if you continue to use your code as-is without providing a
toHash is that it will give the same hash as when you use hashOf directly on
S and give you that deprecation message, but once the deprecation message
becomes an error, Nullable's toHash will start using the typeid workaround
that it currently has, which will cause the value of toHash to then be
incorrect. Of course, if you're not actually using toHash anywhere, that's
not necessarily a big deal, but your opEquals is still probably broken, and
you'll have to live with the deprecation message if you don't provide a
toHash which will work with Nullable's toHash.

So, if you want correct behavior (and no deprecation message), then you'll
need to define an opEquals and toHash which do the correct thing for your
type, _and_ you'll need its toHash to be @safe, const, and nothrow to work
around the bug in Nullable's toHash. Once Nullable's toHash is fixed, the
attribute issue will go away though, and you should then be able to not have
those attributes on your toHash if you don't want to.

- Jonathan M Davis






More information about the Digitalmars-d-learn mailing list