Skip to content

Type.fixTo: Always propagate the naked type to new variants#22852

Open
ibuclaw wants to merge 1 commit intodlang:stablefrom
ibuclaw:propagate_naked
Open

Type.fixTo: Always propagate the naked type to new variants#22852
ibuclaw wants to merge 1 commit intodlang:stablefrom
ibuclaw:propagate_naked

Conversation

@ibuclaw
Copy link
Copy Markdown
Member

@ibuclaw ibuclaw commented Apr 1, 2026

Previously this was only done when the new type was constructed from the main (naked) variant type[1].

However this can result in situations where multiple distinct types are created of the same type.

i.e: Given the mcache map.

T -> sto -> shared(T)
            shared(T) -> sto -> T
            shared(T) -> wsto -> wild-shared(T)
                                 wild-shared(T) -> sto -> shared(T)

If you call shared(T).castMod(0), you get the original T, as the back reference shared(T).mcache.sto exists.

However, if you call wild-shared(T).castMod(0), then there are two different possible outcomes.

  1. When calling wild-shared(T).mutableOf().unSharedOf(), then we successfully get the original T via the back references wild-shared(T).mcache.sto.mcache.sto

  2. When calling wild-shared(T).unSharedOf().mutableOf() (and this is the case in castMod[2]), then a new variant wild(T) type is constructed and fixed to wild-shared(T) only.

T -> sto -> shared(T)
            shared(T) -> sto -> T
            shared(T) -> wsto -> wild-shared(T)
                                 wild-shared(T) -> sto -> shared(T)
                                 wild-shared(T) -> wto -> wild(T)
                                                          wild(T) -> wsto -> wild-shared(T)

Then wild(T).mutableOf() creates a new distinct copy of the original T because wild(T).mcache.wto is null.

It's at this point where Type.merge/merge2 should kick in and discard the second copy. But this only succeeds if T.deco was added into the global type table Type.stringtable before castMod was called! ❗

If lookup(T.deco) returns null, then we're stuck with a split-brain situation of two T's floating around the AST.


What this patch does is to ensure the mcache map always gets populated with the "naked" type (if constructed) when fixing two variant types together, so the mcache map in the above scenario instead ends up looking like:

T -> sto -> shared(T)
            shared(T) -> sto -> T
            shared(T) -> wsto -> wild-shared(T)
                                 wild-shared(T) -> sto -> shared(T)
                                 wild-shared(T) -> wsto -> T
                                 wild-shared(T) -> wto -> wild(T)
                                                          wild(T) -> wsto -> wild-shared(T)
                                                          wild(T) -> wto -> T

So it doesn't matter which variant we have a reference to, we can always get the original "naked" type without having to rely on merge/merge2.

@dlang-bot
Copy link
Copy Markdown
Contributor

Thanks for your pull request, @ibuclaw!

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 "stable + dmd#22852"

@ibuclaw
Copy link
Copy Markdown
Member Author

ibuclaw commented Apr 1, 2026

@kinke - looks reasonable to you?

@ibuclaw ibuclaw marked this pull request as draft April 1, 2026 13:03
@ibuclaw
Copy link
Copy Markdown
Member Author

ibuclaw commented Apr 1, 2026

As the failures suggest, I think this found a bug / typo in one of the xxxxOf helpers!

t = type.mcache.swcto; // shared wild const -> shared
else
t = type.mcache.sto; // shared const => shared
t = type.mcache.sto; // shared (wild) const => shared
Copy link
Copy Markdown
Member Author

@ibuclaw ibuclaw Apr 1, 2026

Choose a reason for hiding this comment

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

Found bug. The swcto back reference of a shared-wild-const type is "naked", not "shared".

It has never been used to hold the reference to the "shared" type, that is in sto instead.

@ibuclaw ibuclaw force-pushed the propagate_naked branch 3 times, most recently from ea600a3 to 59d0ce8 Compare April 1, 2026 14:31
@ibuclaw ibuclaw marked this pull request as ready for review April 1, 2026 14:43
@ibuclaw ibuclaw added the Compiler:GDC Gnu D Compiler label Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Compiler:GDC Gnu D Compiler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants