Thread overview
Feature request: Attribute with which to enable the requirement of explicit-initialization of enum variables
Jun 03, 2013
Andrej Mitrovic
Jun 03, 2013
Diggory
Jun 03, 2013
Jonathan M Davis
Jun 03, 2013
Maxim Fomin
Jun 03, 2013
Diggory
Jun 03, 2013
Maxim Fomin
Jun 03, 2013
Diggory
Jun 03, 2013
Maxim Fomin
Jun 03, 2013
Diggory
June 03, 2013
Let's say you define an enum, which is to be used as a variable:

enum Machine
{
    X86,
    X86_64,
}

So off you go and let the users (or yourself) use this in code:

-----
void main()
{
    Machine machine;
    ...
    callSomething(machine);
}
-----

And here's the problem: the enum variable was default-initialized to the first value. This could either be deliberate, or in many other cases it could be an issue of forgotten initialization.

If it's the latter, then this might become a problem. For example, a library writer could update the enum and inject a new enum member at the first position (before X86). This would update any default-initialized enums in user-code to become something else, and this may cause logical bugs.

Because of this, what I usually do when I define enums is to inject an "Invalid" member as a sentinel at the first position of the enum:

enum Machine
{
    Invalid,  // sentinel
    X86,
    X86_64,
}

Then, if the user really does forget to initialize the enum I will make sure I throw an exception when I run a `final switch` on the enum variable in the functions I provide (when I encounter the Invalid sentinel).  However, the user would have to do this in his own functions as well to make everything perfectly safe.

So, the above doesn't really scale.

What I propose is to add the ability to mark an enum somehow (e.g. with a "required initializer attribute"), so the user will have to manually initialize the enum instead of relying on default-initialization. Default-initialization would fail to compile for this type of enum.

Think of it as the mirror feature of "@disable this" of structs. To provide an example:

@RequireInit enum Machine
{
    X86,
    X86_64,
}

void main()
{
    // Machine machine;  // compile-time error: Machine cannot be
default-initialized

    Machine machine = Machine.X86_64;  // ok, explicitly initialized
}

Thoughts?
June 03, 2013
On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
> Let's say you define an enum, which is to be used as a variable:
>
> enum Machine
> {
>     X86,
>     X86_64,
> }
>
> So off you go and let the users (or yourself) use this in code:
>
> -----
> void main()
> {
>     Machine machine;
>     ...
>     callSomething(machine);
> }
> -----
>
> And here's the problem: the enum variable was default-initialized to
> the first value. This could either be deliberate, or in many other
> cases it could be an issue of forgotten initialization.
>
> If it's the latter, then this might become a problem. For example, a
> library writer could update the enum and inject a new enum member at
> the first position (before X86). This would update any
> default-initialized enums in user-code to become something else, and
> this may cause logical bugs.
>
> Because of this, what I usually do when I define enums is to inject an
> "Invalid" member as a sentinel at the first position of the enum:
>
> enum Machine
> {
>     Invalid,  // sentinel
>     X86,
>     X86_64,
> }
>
> Then, if the user really does forget to initialize the enum I will
> make sure I throw an exception when I run a `final switch` on the enum
> variable in the functions I provide (when I encounter the Invalid
> sentinel).  However, the user would have to do this in his own
> functions as well to make everything perfectly safe.
>
> So, the above doesn't really scale.
>
> What I propose is to add the ability to mark an enum somehow (e.g.
> with a "required initializer attribute"), so the user will have to
> manually initialize the enum instead of relying on
> default-initialization. Default-initialization would fail to compile
> for this type of enum.
>
> Think of it as the mirror feature of "@disable this" of structs. To
> provide an example:
>
> @RequireInit enum Machine
> {
>     X86,
>     X86_64,
> }
>
> void main()
> {
>     // Machine machine;  // compile-time error: Machine cannot be
> default-initialized
>
>     Machine machine = Machine.X86_64;  // ok, explicitly initialized
> }
>
> Thoughts?

Sounds like the exact same feature as "@disable this()" but for enums, so perhaps it would be better to follow that syntax?
June 03, 2013
On Monday, June 03, 2013 05:27:03 Diggory wrote:
> Sounds like the exact same feature as "@disable this()" but for enums, so perhaps it would be better to follow that syntax?

Except that disable this() is specifically for cases where a type _can't_ have a default value and do what it's designed to do (like non-nullable references). I don't think that that same logic applies to an enum.

- Jonathan M Davis
June 03, 2013
On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
> Let's say you define an enum, which is to be used as a variable:
> ...
>
> Thoughts?

I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of @disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).
June 03, 2013
On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
> On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
>> Let's say you define an enum, which is to be used as a variable:
>> ...
>>
>> Thoughts?
>
> I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of @disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).

It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.

June 03, 2013
On Monday, 3 June 2013 at 11:12:10 UTC, Diggory wrote:
> On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
>> On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
>>> Let's say you define an enum, which is to be used as a variable:
>>> ...
>>>
>>> Thoughts?
>>
>> I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of @disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).
>
> It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.

This is again using wrong terminology to move meaning from type to pointed data (if any) as happened recently with dynamic arrays. Nothing on the Earth promises that if in one language class type is allocated memory, than in another language class should be also so, and if it is not, then hoards of programmist should use first naming conversion with no reason. Consult the spec what class type is in D and please do not confuse D with other languages.

Anyway, this irrelevant here, because what I mean is:

class A
{
	@disable this(); // or @RequireInit
}

A a; // does not work

Currently @disable prevents allocation with specified ctor, but does not stops from creating null initialized object. Giving demand for non-nullable classes, probably it is a good idea to support this feature by broading @disable this in context of classes or creating similar feature from scrath like @RequireInit. This issue with classes is more important than with enums, and if such feature is implemented, I see no reason for it not to work with enums as with other user-defined types. And if consensus is that the feature in classes is not need, then it is likely less needed in enums.
June 03, 2013
On Monday, 3 June 2013 at 12:13:30 UTC, Maxim Fomin wrote:
> On Monday, 3 June 2013 at 11:12:10 UTC, Diggory wrote:
>> On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
>>> On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
>>>> Let's say you define an enum, which is to be used as a variable:
>>>> ...
>>>>
>>>> Thoughts?
>>>
>>> I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of @disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).
>>
>> It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.
>
> This is again using wrong terminology to move meaning from type to pointed data (if any) as happened recently with dynamic arrays. Nothing on the Earth promises that if in one language class type is allocated memory, than in another language class should be also so, and if it is not, then hoards of programmist should use first naming conversion with no reason. Consult the spec what class type is in D and please do not confuse D with other languages.

My point is completely applicable to D - it applies to any form of polymorphic type. In D the type of a class variable is determined at runtime, not at compile time, so what you're saying makes no sense.

The feature you want is exactly what NotNull!T does, the way you are suggesting of implementing it doesn't work.
June 03, 2013
On Monday, 3 June 2013 at 16:46:15 UTC, Diggory wrote:
> On Monday, 3 June 2013 at 12:13:30 UTC, Maxim Fomin wrote:
>> On Monday, 3 June 2013 at 11:12:10 UTC, Diggory wrote:
>>> On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
>>>> On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
>>>>> Let's say you define an enum, which is to be used as a variable:
>>>>> ...
>>>>>
>>>>> Thoughts?
>>>>
>>>> I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of @disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).
>>>
>>> It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.
>>
>> This is again using wrong terminology to move meaning from type to pointed data (if any) as happened recently with dynamic arrays. Nothing on the Earth promises that if in one language class type is allocated memory, than in another language class should be also so, and if it is not, then hoards of programmist should use first naming conversion with no reason. Consult the spec what class type is in D and please do not confuse D with other languages.
>
> My point is completely applicable to D - it applies to any form of polymorphic type. In D the type of a class variable is determined at runtime, not at compile time, so what you're saying makes no sense.

No, this is completely wrong. D has static type system and type of expression is determined at compile time.

import std.stdio;

class A {}

class B : A { }

void foo(T) (T t) if (is(T == class))
{ }

void main()
{
	A a;
	foo(a); // belongs to class types irrespective to
	        // allocation and polymorphic type
	a = new B;
	pragma(msg, typeof(a));  // static type system - prints A
	writeln(typeof(a).stringof); // static type system - prints A

}

in addition with supporting runtime polymorphism. You completely confuse language type system with polymorphism and allocation state as well as misunderstanding caused by confusion with an official language spec.

June 03, 2013
On Monday, 3 June 2013 at 17:36:13 UTC, Maxim Fomin wrote:
> On Monday, 3 June 2013 at 16:46:15 UTC, Diggory wrote:
>> On Monday, 3 June 2013 at 12:13:30 UTC, Maxim Fomin wrote:
>>> On Monday, 3 June 2013 at 11:12:10 UTC, Diggory wrote:
>>>> On Monday, 3 June 2013 at 05:56:42 UTC, Maxim Fomin wrote:
>>>>> On Monday, 3 June 2013 at 02:23:18 UTC, Andrej Mitrovic wrote:
>>>>>> Let's say you define an enum, which is to be used as a variable:
>>>>>> ...
>>>>>>
>>>>>> Thoughts?
>>>>>
>>>>> I think it is simpler to set a first enum member as invalid. However, I like an idea of supporting analogue of @disable this() mark for any user-defined types, not structs (I mean it would be pretty good if such feature applied on classes could stop creating null references - it's actually not adding new feature, but increasing scope of existing feature).
>>>>
>>>> It's completely meaningless on classes: it's already impossible to create an instance of a class which is null, because if it's null it's not an instance of the class in the first place.
>>>
>>> This is again using wrong terminology to move meaning from type to pointed data (if any) as happened recently with dynamic arrays. Nothing on the Earth promises that if in one language class type is allocated memory, than in another language class should be also so, and if it is not, then hoards of programmist should use first naming conversion with no reason. Consult the spec what class type is in D and please do not confuse D with other languages.
>>
>> My point is completely applicable to D - it applies to any form of polymorphic type. In D the type of a class variable is determined at runtime, not at compile time, so what you're saying makes no sense.
>
> No, this is completely wrong. D has static type system and type of expression is determined at compile time.

No that's wrong, the static type only determines the highest class in the class hierarchy that can be stored, it does not determine the actual runtime type of the expression.

If you try to implement "@disable this" using the static type it breaks all the rules of covariance and contravariance that classes are expected to follow, and thus breaks the type system. That's why it's implemented as NotNull!T.