Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Private imports can create conflicts with public imports when using selective imports #18223

Open
dlangBugzillaToGithub opened this issue Mar 26, 2024 · 2 comments

Comments

@dlangBugzillaToGithub
Copy link

dlangBugzillaToGithub commented Mar 26, 2024

Jonathan M Davis (@jmdavis) reported this on 2024-03-26T05:20:08Z

Transferred from https://issues.dlang.org/show_bug.cgi?id=24451

Description

Okay. This is a bit weird, but if we have

--- foo/bar.d ---
module foo.bar;

bool empty(T)(T[] arr)
{
    return arr.length == 0;
}
---

--- foo/package.d ---
module foo;

import baz;

public import foo.bar;
---

--- baz.d ---
module baz;

bool empty(T)(T[] arr)
{
    return arr.length == 0;
}
---

--- q.d ---
import foo : empty;

void main()
{
    int[] arr;
    auto result = arr.empty;
}
---

This results in

q.d(6): Error: `empty` matches conflicting symbols:
foo/bar.d(3):        function `foo.bar.empty!int.empty`
baz.d(3):        function `baz.empty!int.empty

If the import in q.d is changed to import foo; then the problem goes away.
So, something about how the selective import works makes it so that the symbols from the private import are in the overload set.

I don't know how likely it is for this situation to come up in practice is, but the way that I ran into it was by trying to add the range API functions for arrays to object.d to see what would happen.
Unsurprisingly, there were a bunch of symbol conflicts, but surprisingly, I couldn't fix the symbol conflicts with selective imports from std.range, e.g.

import std.range : empty;

didn't fix anything. On the other hand, selective imports from std.range.primitives did resolve the conflicts, e.g.

import std.range.primitives : empty;

So, something about the public import was screwing up the selective import, and the test case here is what I came up with.
With empty being in object.d, it's implicitly imported just like happens in the example here with baz being explicitly imported.

In any case, I don't know how likely it is that this problem would come up normally, but it will affect any conflicts with symbols that we add to object.d in the future, and it affects any symbols that are in there now.
It's just that it would only come up when public imports are involved, and they're less common, though it will definitely cause problems if we ever add the current range API functions to object.d.

@0xEAB
Copy link
Member

0xEAB commented Jan 5, 2025

The following code fails to compile because the selectively imported core.internal.traits.AliasSeq from within core.time bleeds through:

alias foo = AliasSeq!(int);
import core.time, std.meta;
demo.d(1): Error: `AliasSeq` matches conflicting symbols:
alias foo = AliasSeq!(int);
            ^
std/meta.d(89):        template `std.meta.AliasSeq(TList...)`
alias AliasSeq(TList...) = TList;
^
core/internal/traits.d(11):        template `core.internal.traits.AliasSeq(TList...)`
alias AliasSeq(TList...) = TList;
^

Originally reported by @crazymonkyyy over there: dlang/phobos#10610

@adamdruppe
Copy link
Contributor

the bug is in alias (note selective import is just syntax sugar for static import + alias)

import b314c;
alias test2 = b314c.test;

void main() {
        assert(b314c.test() == false); // this is fine
        assert(test2() == false); // this complains Error: `test` matches conflicting symbols:
}

weird.

kinke pushed a commit to ldc-developers/ldc that referenced this issue Jan 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants