Jump to page: 1 2
Thread overview
[Issue 17351] Static const array can't be evaluated at compile time when passed as ref argument
Apr 29, 2017
ag0aep6g@gmail.com
Apr 29, 2017
ZombineDev
Apr 29, 2017
ag0aep6g@gmail.com
May 08, 2017
Mathias Lang
Sep 04, 2018
kinke@gmx.net
Aug 10, 2020
Dlang Bot
Aug 13, 2020
Dlang Bot
April 26, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

Andrei Alexandrescu <andrei@erdani.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |andrei@erdani.com

--- Comment #1 from Andrei Alexandrescu <andrei@erdani.com> ---
This issue is not particular to arrays. The following code also fails to compile:

bool fun(S)(ref S a) { return true; }     // (1)
void main()
{
    static const int sa2 = 1;
    static assert(fun(sa2));
}

--
April 28, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

uplink.coder@googlemail.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
                 CC|                            |uplink.coder@googlemail.com
         Resolution|---                         |INVALID

--- Comment #2 from uplink.coder@googlemail.com ---
This code demands an Lvalue to be used at ctfe.
remove the ref from fun and it will work.

We cannot allow the ref-fun to compile.

--
April 29, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

Andrei Alexandrescu <andrei@erdani.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |---

--- Comment #3 from Andrei Alexandrescu <andrei@erdani.com> ---
Please let's keep this opened until we get to a resolution, thanks.

There are several issues here. First, consider this variation:

bool fun(const int a) { return true; }
bool gun(const int[3] a) { return true; }

void main()
{
    static const int x1 = 1;
    static assert(fun(x1));
    static const int[3] x;
    static assert(gun(x));
}

The int goes through, the int[3] does not although the pass by value protocol is the same for both. Apparently float is also handled the same as int. At a minimum we should include in the specification what types enjoy such special treatment.

The second experiment:

bool fun(const int a) { return true; }
bool fun(ref const int a) { return true; }

void main()
{
    static const int x1 = 1;
    static assert(fun(x1));
}

Here, the code attempts to avoid the problem by overloading on ref. However, the call is resolved to the ref version even though it does not go through. This should definitely be fixed, otherwise we're in the position of making a workaround impossible.

The third experiment:

bool fun(T)(auto ref const T a) { return true; }

void main()
{
    static const int x1 = 1;
    static assert(fun(x1));
}

Here, the code gives complete leeway to the compiler as to what version should be called. Again, the compiler chooses the ref version to then refuse to let it go through.

--
April 29, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

--- Comment #4 from uplink.coder@googlemail.com ---
As far as I can see there is no special treatment.

CTFE does only work on literals results of ctfe evaluations are always literals.

--
April 29, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

ag0aep6g@gmail.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ag0aep6g@gmail.com

--- Comment #5 from ag0aep6g@gmail.com ---
(In reply to Andrei Alexandrescu from comment #3)
> bool fun(const int a) { return true; }
> bool gun(const int[3] a) { return true; }
> 
> void main()
> {
>     static const int x1 = 1;
>     static assert(fun(x1));
>     static const int[3] x;
>     static assert(gun(x));
> }
> 
> The int goes through, the int[3] does not although the pass by value protocol is the same for both.

You have to initialize the int[3], as you did with the int. I suppose the compiler assumes that you're going to do run-time initialization via `static this` when you're not initializing in the declaration. In that case, the value can't be used at compile time, obviously.

[...]
> bool fun(const int a) { return true; }
> bool fun(ref const int a) { return true; }
> 
> void main()
> {
>     static const int x1 = 1;
>     static assert(fun(x1));
> }
> 
> Here, the code attempts to avoid the problem by overloading on ref. However, the call is resolved to the ref version even though it does not go through. This should definitely be fixed, otherwise we're in the position of making a workaround impossible.

You can work around by using an enum instead of a static const. That makes x1 an rvalue, and the ref overload isn't involved anymore.

It might be worthwhile to require enums for compile-time constants; i.e., ban non-enum consts/immutables. The const/immutable qualifiers simply don't imply compile-time constancy. enum does.

That would be a breaking change, of course. And it would break benign code such as `static const a = 1; static const b = a + 1;`. But a clean cut may be better than confusing special cases.

--
April 29, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

--- Comment #6 from Andrei Alexandrescu <andrei@erdani.com> ---
(In reply to ag0aep6g from comment #5)
> You have to initialize the int[3], as you did with the int. I suppose the compiler assumes that you're going to do run-time initialization via `static this` when you're not initializing in the declaration. In that case, the value can't be used at compile time, obviously.

Cool. Indeed this works even with structs:

bool fun(const int a) { return true; }
bool gun(const int[3] a) { return true; }
bool hun(const S a) { return true; }

struct S { int a; }

void main()
{
    static const int x1 = 1;
    static assert(fun(x1));
    static const int[3] x = [1, 2, 3];
    static assert(gun(x));
    static const S x2 = S(1);
    static assert(hun(x2));
}

So we're in good shape.

> [...]
> > bool fun(const int a) { return true; }
> > bool fun(ref const int a) { return true; }
> > 
> > void main()
> > {
> >     static const int x1 = 1;
> >     static assert(fun(x1));
> > }
> > 
> > Here, the code attempts to avoid the problem by overloading on ref. However, the call is resolved to the ref version even though it does not go through. This should definitely be fixed, otherwise we're in the position of making a workaround impossible.
> 
> You can work around by using an enum instead of a static const.

Not an option. The context is the following. We're looking at lowering all array comparisons like this:

e1 == e2 -----> __equals(e1, e2)
e1 != e2 -----> !__equals(e1, e2)

Then implement __equals as a template in object.d. There are a number of challenges related to that, which we solved together with Walter. The current approach defines these overloads:

bool __equals(L, R)(L[] lhs, R[] rhs);
bool __equals(L, R, n1)(auto ref L[n1] lhs, R[] rhs);
bool __equals(L, R, n2)(auto ref L[] lhs, auto ref R[n2] rhs);
bool __equals(L, R, n1, n2)(auto ref L[n1] lhs, auto ref R[n2] rhs);

This approach has a number of benefits compared to the status quo, among which much better speed (and as a perk fewer dynamic allocations for array literals when passed in comparisons such as arr == [1, 2, 3]).

The auto ref is necessary so we don't copy arrays into the comparison functions, yet we still work with rvalue arrays.

Now, Lucia (@somzzz) has gotten to the point where it all passes druntime and phobos unittests, but breaks in exactly one point in the compiler. It can be reduced to this:

static const int[3] x = [1, 2, 3];
static assert(x == x);

Here, the compiler chooses the "ref" version even though it is not compilable. The version with rvalues should be taken and used.

--
April 29, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

--- Comment #7 from Andrei Alexandrescu <andrei@erdani.com> ---
I meant:

bool __equals(L, R)(L[] lhs, R[] rhs);
bool __equals(L, R, size_t n1)(auto ref L[n1] lhs, R[] rhs);
bool __equals(L, R, size_t n2)(auto ref L[] lhs, auto ref R[n2] rhs);
bool __equals(L, R, size_t n1, size_t n2)(auto ref L[n1] lhs, auto ref R[n2]
rhs);

(There are also constraints on L and R that I omitted.)

--
April 29, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

ZombineDev <petar.p.kirov@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |petar.p.kirov@gmail.com
           Hardware|x86_64                      |All
                 OS|Linux                       |All
           Severity|normal                      |blocker

--
April 29, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

--- Comment #8 from ag0aep6g@gmail.com ---
(In reply to Andrei Alexandrescu from comment #6)
> Now, Lucia (@somzzz) has gotten to the point where it all passes druntime and phobos unittests, but breaks in exactly one point in the compiler. It can be reduced to this:
> 
> static const int[3] x = [1, 2, 3];
> static assert(x == x);

Got it. You need the special overload behavior to keep that assert working with the new implementation. I'm not particularly opposed to tweaking overload resolution to that end.

However, as mentioned, another option could be to disallow that static assert on the grounds that x being static const doesn't mean it's a compile-time constant. I realize that this would be a relatively disruptive change, but it would simplify the language instead of complicating it further.

It would also make this little gem impossible:

----
void main()
{
    const bool x = __ctfe;
    static const bool y = x;
    static assert(x == y); /* passes */
    assert(x == y); /* fails */
}
----

--
May 08, 2017
https://issues.dlang.org/show_bug.cgi?id=17351

Mathias Lang <mathias.lang@sociomantic.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |mathias.lang@sociomantic.co
                   |                            |m

--
« First   ‹ Prev
1 2