www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - enum pointers or class references limitation

reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
The subj is not (any longer) supported by compiler. In fact it 
used to produce wrong code sometimes and now it just plainly 
rejects it.

It's freaking inconvenient because I can't deploy new 
compile-time std.regex w/o it.

The example:

enum ctr = ctRegex!"blah";

after my changes must be:

static immutable ctr = ctRegex!"blah";

Howeever I divised a trick to get equivalent functionality as 
follows:

template ctRegexImpl(alias pattern, string flags=[])
{
    static immutable staticRe = ...;
    struct Wrapper
    {
        property auto getRe(){ return staticRe; }
       alias getRe this;
    }
    enum wrapper = Wrapper();
}

public enum ctRegex(alias pattern, alias flags=[]) = 
ctRegexImpl!(pattern, flags).wrapper;

Now ctRegex returns a neat forwarding struct that bypasses the 
strange limitation. The question remains though - why can't 
compiler do it automatically?
Aug 30
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 30.08.2017 11:36, Dmitry Olshansky wrote:
 The subj is not (any longer) supported by compiler. In fact it used to 
 produce wrong code sometimes and now it just plainly rejects it.
 
 It's freaking inconvenient because I can't deploy new compile-time 
 std.regex w/o it.
 
 The example:
 
 enum ctr = ctRegex!"blah";
 
 after my changes must be:
 
 static immutable ctr = ctRegex!"blah";
 
 Howeever I divised a trick to get equivalent functionality as follows:
 
 template ctRegexImpl(alias pattern, string flags=[])
 {
     static immutable staticRe = ...;
     struct Wrapper
     {
         property auto getRe(){ return staticRe; }
        alias getRe this;
     }
     enum wrapper = Wrapper();
 }
 
 public enum ctRegex(alias pattern, alias flags=[]) = 
 ctRegexImpl!(pattern, flags).wrapper;
 
 Now ctRegex returns a neat forwarding struct that bypasses the strange 
 limitation. The question remains though - why can't compiler do it 
 automatically?
 
I think the underlying reason why it does not work is that dynamic array manifest constants are messed up. I.e. class reference `enum`s are disallowed in order to avoid having to make a decision for either inconsistent or insane semantics.
Aug 30
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Wednesday, 30 August 2017 at 12:28:10 UTC, Timon Gehr wrote:
 On 30.08.2017 11:36, Dmitry Olshansky wrote:
 The subj is not (any longer) supported by compiler. In fact it 
 used to produce wrong code sometimes and now it just plainly 
 rejects it.
 
[..]
 I think the underlying reason why it does not work is that 
 dynamic array manifest constants are messed up. I.e. class 
 reference `enum`s are disallowed in order to avoid having to 
 make a decision for either inconsistent or insane semantics.
Well from my point of view enum is just evaluate this expression at the usage site. So any array or class instance will be created anew at the point of usage. What are the problems with enums and dynamic arrays?
Aug 31
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Thursday, 31 August 2017 at 08:40:03 UTC, Dmitry Olshansky 
wrote:
 On Wednesday, 30 August 2017 at 12:28:10 UTC, Timon Gehr wrote:
 On 30.08.2017 11:36, Dmitry Olshansky wrote:
 The subj is not (any longer) supported by compiler. In fact 
 it used to produce wrong code sometimes and now it just 
 plainly rejects it.
 
[..]
 I think the underlying reason why it does not work is that 
 dynamic array manifest constants are messed up. I.e. class 
 reference `enum`s are disallowed in order to avoid having to 
 make a decision for either inconsistent or insane semantics.
Well from my point of view enum is just evaluate this expression at the usage site. So any array or class instance will be created anew at the point of usage. What are the problems with enums and dynamic arrays?
I think Timon is referring to: enum int[] foo = [1,2,3]; auto bar = foo; auto baz = foo; assert(!(bar is baz)); // Passes
Aug 31
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 08/31/2017 01:52 AM, Nicholas Wilson wrote:

 I think Timon is referring to:

 enum int[] foo = [1,2,3];

 auto bar = foo;
 auto baz = foo;

 assert(!(bar is baz)); // Passes
Even better: enum int[] foo = [1,2,3]; assert(!(foo is foo)); // Passes Ali
Aug 31
parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Thursday, 31 August 2017 at 14:28:57 UTC, Ali Çehreli wrote:
 On 08/31/2017 01:52 AM, Nicholas Wilson wrote:

 I think Timon is referring to:

 enum int[] foo = [1,2,3];

 auto bar = foo;
 auto baz = foo;

 assert(!(bar is baz)); // Passes
Even better: enum int[] foo = [1,2,3]; assert(!(foo is foo)); // Passes
I guess assert(!([1,2,3] is [1,2,3])); Which is exactly what enum expands to and totally expected. Where is the surprise?
 Ali
Sep 01
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 09/01/2017 11:48 AM, Dmitry Olshansky wrote:
 On Thursday, 31 August 2017 at 14:28:57 UTC, Ali Çehreli wrote:
 On 08/31/2017 01:52 AM, Nicholas Wilson wrote:

 I think Timon is referring to:

 enum int[] foo = [1,2,3];

 auto bar = foo;
 auto baz = foo;

 assert(!(bar is baz)); // Passes
Even better: enum int[] foo = [1,2,3]; assert(!(foo is foo)); // Passes
I guess assert(!([1,2,3] is [1,2,3])); Which is exactly what enum expands to and totally expected. Where is the surprise?
In the surprising case foo is a symbol, seemingly of a variable. Failing the 'is' test is surprising in that case. I've just remembered that the actual surprising case is the following explicit check: assert(!(foo.ptr is foo.ptr)); // Passes I find it surprising because it looks like an entity does not have a well-behaving .ptr. (Aside: I think your code might be surprising to at least newcomers as well.) Ali
Sep 01
parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Friday, 1 September 2017 at 21:08:20 UTC, Ali Çehreli wrote:
 [snip]
 assert(!([1,2,3] is [1,2,3]));

 Which is exactly what enum expands to and totally expected.
Where is the
 surprise?
This is not a surprise. Array literals are not identical.
 In the surprising case foo is a symbol, seemingly of a 
 variable. Failing the 'is' test is surprising in that case. 
 I've just remembered that the actual surprising case is the 
 following explicit check:

     assert(!(foo.ptr is foo.ptr)); // Passes

 I find it surprising because it looks like an entity does not 
 have a well-behaving .ptr.

 (Aside: I think your code might be surprising to at least 
 newcomers as well.)
That's a good reason to unrecommend/disallow enums with indirections. The compiler should recommend/suggest using static immutable instead as it does not have such oddities. The only advantage of enum is being guaranteed to be known at compile-time and they can be templatized (can be also done for static immutable via eponymous template). I'd vote for a warning/error when the type of an enum has indirections together with a pragma to switch the warning off for the rare case you know exactly what you do. Just as Scott Meyers said: make it easy to use correctly and hard to use incorrectly. Today it's easy to use incorrectly.
Sep 01
next sibling parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Friday, 1 September 2017 at 23:13:50 UTC, Q. Schroll wrote:
 [..]
 Just as Scott Meyers said: make it easy to use correctly and 
 hard to use incorrectly. Today it's easy to use incorrectly.
While enum foo = [1,2,3]; assert(foo is foo); fails, enum bla = "123"; assert(foo is foo); passes. Enhancement request submitted: https://issues.dlang.org/show_bug.cgi?id=17799 Unfortunately after I found out the second one does not have to do with mutability. Making foo immutable(int)[] does not change anything. It only works for const(char)[], immutable(char)[], and probably w/dchar friends. That's odd.
Sep 01
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02.09.2017 01:37, Q. Schroll wrote:
 On Friday, 1 September 2017 at 23:13:50 UTC, Q. Schroll wrote:
 [..]
 Just as Scott Meyers said: make it easy to use correctly and hard to 
 use incorrectly. Today it's easy to use incorrectly.
While enum foo = [1,2,3]; assert(foo is foo); fails, enum bla = "123"; assert(foo is foo); passes. Enhancement request submitted: https://issues.dlang.org/show_bug.cgi?id=17799 Unfortunately after I found out the second one does not have to do with mutability. Making foo immutable(int)[] does not change anything. It only works for const(char)[], immutable(char)[], and probably w/dchar friends. That's odd.
This is called string pooling. This passed too: void main(){ assert("123" is "123"); } D (at least sometimes) allows the identities of different immutable locations to become conflated.
Sep 01
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 02.09.2017 01:13, Q. Schroll wrote:
 On Friday, 1 September 2017 at 21:08:20 UTC, Ali Çehreli wrote:
 [snip]
 assert(!([1,2,3] is [1,2,3]));

 Which is exactly what enum expands to and totally expected.
Where is the
 surprise?
This is not a surprise. Array literals are not identical.
 In the surprising case foo is a symbol, seemingly of a variable. 
 Failing the 'is' test is surprising in that case. I've just remembered 
 that the actual surprising case is the following explicit check:

     assert(!(foo.ptr is foo.ptr)); // Passes

 I find it surprising because it looks like an entity does not have a 
 well-behaving .ptr.

 (Aside: I think your code might be surprising to at least newcomers as 
 well.)
That's a good reason to unrecommend/disallow enums with indirections.
It can be useful to have enums with indirections available for metaprogramming, for intermediate data typed as mutable that is not necessarily required while running the program, yet can be read during compilation.
 The compiler should recommend/suggest using static immutable instead as 
 it does not have such oddities.
It types your data as immutable.
 The only advantage of enum is being 
 guaranteed to be known at compile-time
That is not an advantage of enum, as the same is true for static immutable.
 and they can be templatized (can 
 be also done for static immutable via eponymous template).
 I'd vote for a warning/error when the type of an enum has indirections 
 together with a pragma to switch the warning off for the rare case you 
 know exactly what you do.
 Just as Scott Meyers said: make it easy to use correctly and hard to use 
 incorrectly. Today it's easy to use incorrectly.
 
Note that enums with indirections are already disallowed, except for dynamic arrays.
Sep 01
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 01.09.2017 20:48, Dmitry Olshansky wrote:
 On Thursday, 31 August 2017 at 14:28:57 UTC, Ali Çehreli wrote:
 On 08/31/2017 01:52 AM, Nicholas Wilson wrote:

 I think Timon is referring to:

 enum int[] foo = [1,2,3];

 auto bar = foo;
 auto baz = foo;

 assert(!(bar is baz)); // Passes
Even better: enum int[] foo = [1,2,3]; assert(!(foo is foo)); // Passes
I guess assert(!([1,2,3] is [1,2,3])); Which is exactly what enum expands to and totally expected.
I know what it does and do expect it. I still consider it problematic. It's conceivable that someone just wants an enum slice to statically allocated array data (or a enum struct instance that has a field that is a statically allocated array, etc.). The enum/array literal design makes this impossible.
 Where is the surprise?
My main point is that I don't see why a ctRegex should runtime-allocate a class instance at each usage site. (And if it does not, the difference to array literals is not so easy to justify.) But, there are a number of perhaps surprising behaviors of array literals at compile time that come to mind, some, but not all closely related to the issue at hand: struct S{ int[] x; } static assert(S([1,2,3]) is S([1,2,3])); // ok auto x = S([1,2,3]), y = S([1,2,3]); struct C{ int[] x; this(int[] x){ this.x=x; } } static assert(new C([1,2,3]).x is new C([1,2,3]).x); // ok static assert((){ auto c=new C([1,2,3]); auto d=new C([1,2,3]); assert(c.x is d.x); c.x[0]=2; assert(c.x !is d.x); return true; }()); // ok enum s = S([1,2,3]); immutable t = S([1,2,3]); enum u = t; void main() nogc{ assert(x is y); // fails // auto v = s; // error: gc allocation auto w1 = t; // ok // auto w2 = u; // error (!) } Basically, I think implicitly making expressions confused about their aliasing is just not a good idea. You can see that your: 'enum is just evaluate this expression at the usage site' is not the full story as otherwise w1 and w2 would behave the same.
Sep 01