Comment #0 by qs.il.paperinik — 2023-06-20T14:25:16Z
When function attributes are inferred, they end up in the type:
```d
auto f() {}
static assert(is(typeof(&f) == void function() @safe nothrow @nogc pure));
```
However, when parameters’ `scope` and `return` attributes are inferred, they are not displayed in error messages or `pragma(msg)`.
They might not be displayed, but could still (silently) be part of the type; but in a Schrödinger fashion, they are and they are not:
```d
auto f(int[] xs) @safe
{
static int ctr;
ctr++; // force impure
return xs;
}
void tplTypeInfer(F)(ref F fp1, ref F fp2)
{
int[3] xs;
fp1(xs);
fp2(xs);
}
//void nonTpl(ref int[] function(int[] xs) nothrow @nogc @safe fp) @safe
//{
// //int[3] xs;
// //fp(xs);
//}
void main() @safe
{
int[3] xs;
f(xs); // good: `xs` binds to parameter inferred `return scope`.
static assert(typeof(&f).stringof ==
"int[] function(int[] xs) nothrow @nogc @safe");
alias F1 = int[] function(int[] xs) nothrow @nogc @safe;
alias F2 = typeof(&f);
static assert(is(F1 == F2)); // PASSES!
F1 fp1 = &f; // This and …
F2 fp2 = &f; // … this should behave the same, right?
fp1(xs); // error: reference to local variable `xs` assigned to non-scope parameter `xs`
fp2(xs); // good (apparently, the parameter is scope here)
tplTypeInfer(fp1, fp2); // good/error depending on if nonTpl is commented-in
tplTypeInfer(fp2, fp1); // good/error (same)
tplTypeInfer!F1(fp1, fp2); // good/error (same)
tplTypeInfer!F1(fp2, fp1); // good/error (same)
tplTypeInfer!F2(fp1, fp2); // good/error (same)
tplTypeInfer!F2(fp2, fp1); // good/error (same)
}
```
It seems that if you *use* the function pointer, it remembers somehow that the parameter is inferred scope.
Also, there’s some Schrödinger action going on with template type inference.
Comment #1 by dkorpel — 2023-06-20T15:18:50Z
(In reply to Bolpat from comment #0)
> Also, there’s some Schrödinger action going on with template type inference.
Yes, I'm all too aware of this.
It was decided at some point to make inferred `scope` and `return` not part of the type mangle, so that there wouldn't be link errors between phobos compiled with dip1000 and user code without it.
However, the compiler uses the mangle internally as the identity of a type. So if two types have the same mangled name, they are considered equal.
I tried undoing this, but of course, that breaks everything, so it's going to be hard to untangle the mess.
Comment #2 by dlang-bot — 2023-06-20T16:10:15Z
@dkorpel created dlang/dmd pull request #15333 "Issue 24003 - mangle inferred return/scope attributes in parameters" mentioning this issue:
- Issue 24003 - mangle inferred return/scope attributes in parameters
https://github.com/dlang/dmd/pull/15333
Comment #3 by Ajieskola — 2024-04-23T12:03:17Z
I think this is same as what I was just about to report:
```d
auto nullPtr(int*) => (int*).init;
@safe unittest
{ int local;
// compiles (with -preview=dip1000), because the parameter is correctly inferred as scope
auto nullP = nullPtr(&local);
}
// Yet this says AliasSeq!().
// Should say AliasSeq!"scope".
pragma(msg, __traits(getParameterStorageClasses, nullPtr, 0));
```
I was solving https://issues.dlang.org/show_bug.cgi?id=23300, but this bug is blocking me. Since `scope` inference is limited, `std.array.array` needs to manually introspect whether `.front` / `.opApply` / element copy of the source range are `scope`. With this bug, it only works in the rare few cases where `scope` is manually specified.
I have tried working around it by testing whether a call passing a `scope` argument compiles, but no joy. DIP1000 checks are only done in safe code, and trying to cast a `@system` function to a `@trusted` one to make the check work, without changing the function type in any other way or losing the `scope` inference, was just too difficult for me - probably impossible.
Comment #4 by robert.schadek — 2024-12-13T19:29:46Z