Thread overview
[Issue 13808] Scoped import in struct body hijacks UFCS
[Issue 13808] Scoped std.string:format import in SysTime hijacks UFCS
Dec 04, 2014
Kenji Hara
Dec 06, 2014
Vladimir Panteleev
Aug 06, 2020
Mathias LANG
December 02, 2014
https://issues.dlang.org/show_bug.cgi?id=13808

Steven Schveighoffer <schveiguy@yahoo.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |schveiguy@yahoo.com

--- Comment #1 from Steven Schveighoffer <schveiguy@yahoo.com> ---
I think perhaps the correct change is to import format only inside those functions that need it.

--
December 02, 2014
https://issues.dlang.org/show_bug.cgi?id=13808

hsteoh@quickfur.ath.cx changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |hsteoh@quickfur.ath.cx

--- Comment #2 from hsteoh@quickfur.ath.cx ---
Argh... yet another scoped import pathological behaviour. Apparently putting an import in the body of a struct S pulls symbols into the struct's namespace, so that S.symbol now refers to the imported symbol.

This is really nasty, and greatly limits the usefulness of scoped imports, for example:

-----
// mod.d
module mod;
struct S {
    // This is necessary for myFunc, and we can't move it inside myFunc,
    // because we need it in the sig constraint
    import std.range.primitives : isInputRange;

    auto myFunc(R)(R input)
        if (isInputRange!R)
    { ... }
}

// main.d
import mod;
void main() {
    S s;
    static if (s.isInputRange!(int[])) { // this actually works :-(
       ...
    }
    static if (isInputRange!(int[])) { // whereas this doesn't compile
    }
}
-----

Note that making the import in S static doesn't help, because static imports cannot bind specific symbols, so you have to alias specific import symbols... but that again leaks them to the outside via UFCS.

This is no longer just about std.datetime, this is a glaring hole in the import system.

--
December 02, 2014
https://issues.dlang.org/show_bug.cgi?id=13808

hsteoh@quickfur.ath.cx changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
          Component|Phobos                      |DMD
            Summary|Scoped std.string:format    |Scoped import in struct
                   |import in SysTime hijacks   |body hijacks UFCS
                   |UFCS                        |

--
December 02, 2014
https://issues.dlang.org/show_bug.cgi?id=13808

--- Comment #3 from hsteoh@quickfur.ath.cx ---
Workaround for std.datetime:

https://github.com/D-Programming-Language/phobos/pull/2780

This is not a complete fix, as there are a bunch of other scoped imports as well, and it would take quite a bit more effort to sort all of them out. :-( In any case, it's probably better if we address this in the compiler instead of putting workarounds into every library that uses scoped imports at the struct/class level.

--
December 04, 2014
https://issues.dlang.org/show_bug.cgi?id=13808

--- Comment #4 from Kenji Hara <k.hara.pg@gmail.com> ---
(In reply to hsteoh from comment #2)
> Argh... yet another scoped import pathological behaviour. Apparently putting an import in the body of a struct S pulls symbols into the struct's namespace, so that S.symbol now refers to the imported symbol.

Any imported symbols in struct body scope should not be visible via the struct instance object.

> This is really nasty, and greatly limits the usefulness of scoped imports, for example:
> 
> -----
> // mod.d
> module mod;
> struct S {
>     // This is necessary for myFunc, and we can't move it inside myFunc,
>     // because we need it in the sig constraint
>     import std.range.primitives : isInputRange;
> 
>     auto myFunc(R)(R input)
>         if (isInputRange!R)
>     { ... }
> }
> 
> // main.d
> import mod;
> void main() {
>     S s;
>     static if (s.isInputRange!(int[])) { // this actually works :-(
>        ...
>     }
>     static if (isInputRange!(int[])) { // whereas this doesn't compile
>     }
> }
> -----

My import mechanism improvement will disallow the weird name lookup
`s.isInputRange!(int[])`.

https://github.com/D-Programming-Language/dmd/pull/3407

--
December 04, 2014
https://issues.dlang.org/show_bug.cgi?id=13808

--- Comment #5 from github-bugzilla@puremagic.com ---
Commit pushed to master at https://github.com/D-Programming-Language/phobos

https://github.com/D-Programming-Language/phobos/commit/6473aefc71c6b5c5183fdf150d6528c865a741ad Merge pull request #2780 from quickfur/issue13808

Issue 13808: Don't import std.format in any public scope.

--
December 06, 2014
https://issues.dlang.org/show_bug.cgi?id=13808

Vladimir Panteleev <thecybershadow@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Severity|regression                  |major

--- Comment #6 from Vladimir Panteleev <thecybershadow@gmail.com> ---
Since the reported regression is fixed, I am lowering the severity to conform to the new issue title.

--
February 19, 2015
https://issues.dlang.org/show_bug.cgi?id=13808

--- Comment #7 from github-bugzilla@puremagic.com ---
Commit pushed to 2.067 at https://github.com/D-Programming-Language/phobos

https://github.com/D-Programming-Language/phobos/commit/6473aefc71c6b5c5183fdf150d6528c865a741ad Merge pull request #2780 from quickfur/issue13808

--
August 06, 2020
https://issues.dlang.org/show_bug.cgi?id=13808

Mathias LANG <pro.mathias.lang@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
                 CC|                            |pro.mathias.lang@gmail.com
         Resolution|---                         |FIXED

--- Comment #8 from Mathias LANG <pro.mathias.lang@gmail.com> ---
Just tested the following:

--- foo.d
module foo;

import bar;

string format(MyStruct s) { return "foo"; }

void main()
{
    MyStruct s;
    s.format();
}
--- bar.d
module bar;

struct MyStruct
{
    import std.format : format;

    uint value;
}
---


It works nowadays. I'm pretty sure this was fixed when DIP22 was implemented, aka the two-phases lookup.

--