June 21, 2023
https://issues.dlang.org/show_bug.cgi?id=24004

          Issue ID: 24004
           Summary: UFCS is not uniform/universal (enough)
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: dmd
          Assignee: nobody@puremagic.com
          Reporter: qs.il.paperinik@gmail.com

If `typeof(obj)` has no member `mem`, the syntax `obj.mem(…)` can resolve to
`mem(obj, …)`. Ideally, one does not need to remember if a function is a member
function or a non-member function with an appropriate first parameter.

In the module of `typeof(obj)`, this works perfectly.

Outside the module, one has to import the type and the non-member functions,
either using
```d
import mod;
```
or
```d
import mod : TheType, mem;
```
If only the type is imported, `mem` cannot be resolved.

It’s worse for templates that get access to the type via a template parameter:
```d
void f(T)(T obj)
{
    obj.mem(…); // works if `mem` is a member function
    // OLD:     // fails if `mem` is not a member function
}
```

Of course, the language cannot require that every module in existence be searched for a UFCS call.

What it could do, though, is search the module that defines the type. [Up for
consideration: Additionally, if present, `package.d` files should be searched
for the symbol, but not transitively, i.e. `public import`s are not considered.
(This would lead to too much overhead.]
For all intents and purposes, in D, modules [and packages] are a unit. The
aggregate is not the proper unit to resolve UFCS, but the module [or package]
is.

```d
void f(T)(T obj)
{
    obj.mem(…); // works if `mem` is a member function
    // NEW:     // works if `mem` is a non-member function in the module of `T`
}
```

This means that when one has access to a type, one has access to non-member
functions in the type’s module, but (unless imported) only through UFCS:
```d
import mod : TheType;
TheType obj;
obj.nonmem(…); // works for non-member: Found through UFCS module search
nonmem(obj);   // fails for non-member: `nonmem` is not imported
```

--