The basic use case is having:
foo(const(char)[]);
and trying to pass a
immutable(char)[] to it. Which is illegal.
The problem here is that if you define a manifest constant that is empty, and try to pass it to foo, the code will compile:
//----
void main()
{
void foo(const(char)[][]);
alias Type = string[];
static assert(is(typeof(Type.init) == Type));
static if (is(typeof(foo(Type.init)))) //Enter here !!!
{
Type Rbla() @property;
Type Lbla;
enum Type Ebla1 = [[]];
enum Type Ebla2 = ["hello"];
foo(Type.init); //OK!
foo(Rbla);
foo(Lbla);
foo(Ebla1); //OK!
foo(Ebla2);
}
}
//----
Error: function dmd2.main.foo (const(char)[][]) is not callable using argument types (string[])
Error: cannot implicitly convert expression (Rbla()) of type string[] to const(char)[][]
Error: function dmd2.main.foo (const(char)[][]) is not callable using argument types (string[])
Error: cannot implicitly convert expression (Lbla) of type string[] to const(char)[][]
//----
This may not seem like a "huge" problem in and out of itself. The *REAL* problem lies in the static if:
"static if (is(typeof(foo(Type.init))))"
This code is explicitly written to find out if we can pass a variable of type Type to foo. This is especially true for array types, which are the ones vulnerable to this problem.
I'm marking as "Major", because phobos is vulnerable to the bug.
Found inside "put":
//----
else static if (is(typeof(r.put((E[]).init))))
{
r((&e)[0..1]);
}
//----
src\phobos\std\range.d(590): Error: cannot implicitly convert expression (& e[0u..1u]) of type string[] to const(char)[][]
//----
Current workaround is to use an lvalueof type function.
Comment #1 by andrej.mitrovich — 2012-12-19T07:12:53Z
(In reply to comment #0)
> The basic use case is having:
> foo(const(char)[]);
> and trying to pass a
> immutable(char)[] to it. Which is illegal.
Since when is that illegal? immutable and mutable both implicitly convert to const.
Comment #2 by monarchdodra — 2012-12-19T07:26:55Z
(In reply to comment #1)
> (In reply to comment #0)
> > The basic use case is having:
> > foo(const(char)[]);
> > and trying to pass a
> > immutable(char)[] to it. Which is illegal.
>
> Since when is that illegal? immutable and mutable both implicitly convert to
> const.
Typo. My apologies:
> The basic use case is having:
> foo(const(char)[][]);
> and trying to pass a
> "immutable(char)[][]" to it. Which is illegal.
Comment #3 by k.hara.pg — 2012-12-21T00:30:27Z
This is not a type system problem.
All manifest constants, which declared by `enum` and T.init, make literal expressions in the places where they used.
alias T = string[];
enum T strs = [[]];
foo(T.init); // same as foo(null);
foo(strs); // same as foo([[]]);
And, empty array literal and null literal can ignore their type qualifiers in constant-folding process, because they have no references to qualified values.
(From the view of compiler development, it is mainly designed by Expression::implicitConvTo.)
I can agree that the behavior is not intuitive, but I'm not sure what is the correct behavior...
Comment #4 by samukha — 2012-12-21T01:35:54Z
(In reply to comment #3)
> I'm not sure what is the correct behavior...
Obviously, the correct behavior is to preserve the type information.
Comment #5 by monarchdodra — 2012-12-21T01:52:11Z
(In reply to comment #3)
> This is not a type system problem.
>
> All manifest constants, which declared by `enum` and T.init, make literal
> expressions in the places where they used.
>
> alias T = string[];
> enum T strs = [[]];
> foo(T.init); // same as foo(null);
> foo(strs); // same as foo([[]]);
>
> And, empty array literal and null literal can ignore their type qualifiers in
> constant-folding process, because they have no references to qualified values.
I see your point, but I argue there is a difference between a "type-less" literal and an "empty" literal.
(T[]).init, while empty, does carry a type, and it should be preserved/propagated.
Heck, proof that a non-empty literal can have "no type":
"[[]]" is *not* empty. It is a type-less slice which carries a single empty typeless slice. Yet you can still write "int[][] a = [[]];"
Comment #6 by robert.schadek — 2024-12-13T18:03:31Z