Mutable enums

Steven Schveighoffer schveiguy at yahoo.com
Thu Nov 17 06:19:19 PST 2011


On Wed, 16 Nov 2011 17:39:16 -0500, Timon Gehr <timon.gehr at gmx.ch> wrote:

> On 11/16/2011 10:56 PM, Steven Schveighoffer wrote:
>> On Wed, 16 Nov 2011 16:16:48 -0500, Timon Gehr <timon.gehr at gmx.ch>  
>> wrote:
>>
>>> On 11/16/2011 09:00 PM, Steven Schveighoffer wrote:
>>>> On Wed, 16 Nov 2011 14:26:57 -0500, Timon Gehr <timon.gehr at gmx.ch>
>>>> wrote:
>>>>
>>>>> On 11/16/2011 02:22 PM, Steven Schveighoffer wrote:
>>>>>> On Tue, 15 Nov 2011 13:45:02 -0500, Timon Gehr <timon.gehr at gmx.ch>
>>>>>> wrote:
>>>>>>
>>>>>>> Note that this is an explicit allocation:
>>>>>>>
>>>>>>> int[] a = [1,2,3]; // just as explicit as a NewExpression
>>>>>>>
>>>>>>> Only the enums "hide" it sometimes, but actually you should know  
>>>>>>> the
>>>>>>> involved types.
>>>>>>
>>>>>> As I've said, there are already ways to explicitly allocate memory.  
>>>>>> A
>>>>>> suggested replacement for this is:
>>>>>>
>>>>>> int[] a = array(1, 2, 3);
>>>>>>
>>>>>> And you could always do:
>>>>>>
>>>>>> int[] a = [1, 2, 3].dup;
>>>>>>
>>>>>> Nobody complains about having to do:
>>>>>>
>>>>>> char[] a = "hello".dup;
>>>>>>
>>>>>> I don't see why we couldn't do the same for all array literals.
>>>>>>
>>>>>
>>>>> Because 'immutable' behaves nicely on built-in value types, but not  
>>>>> on
>>>>> arbitrary reference types.
>>>>
>>>> string is a reference type. We hear no complaints about strings being
>>>> stored in ROM and the type of literals being immutable.
>>>>
>>>
>>> string is not an immutable type. It is immutable(char)[] and char is a
>>> built-in value type.
>>>
>>> static assert(isMutable!string);
>>
>> It fits my definition of a valid enum reference type (immutable or
>> implicitly castable to immutable). The point is that the data referenced
>> is stored in ROM and therefore a) immutable and b) fully defined at
>> compile-time.
>
> Indeed. But fact is, the data that is qualified with immutable is not of  
> reference type therefore it behaves nicely. And you don't get that in  
> the general case.

In the general case, there is always a library function for construction.   
In other words, what the compiler currently does for array literals can be  
done in a library.  But a library cannot create ROM space.  The  
compiler-based features (and CTFE in general) should be helping us create  
things at compile time, not at run time.  We already have the tools to  
construct arrays at runtime.

>
> I am looking for something like this:
>
> template writeln(T...)(T){
>      alias writelnImpl!(writelnInferConst!T) writeln;
> }
>
> (it is even trivial to parse, unlike normal function definitions!)

What does writelnInferConst!T do?  I'm afraid I'm not getting what you are  
saying.

I was thinking writeln should do this:

void writeln(T...)(const T args) {...}

>> I have an enhancement request in for intercepting IFTI (not sure if it
>> applies here): http://d.puremagic.com/issues/show_bug.cgi?id=4998
>>
>
> It has a complexity of at least 2^numparams and probably all kinds of  
> odd implications for the compiler internals that would lead to a buggy  
> implementation.

I'm not a compiler writer, but I don't see how this is the case.

>
> I think this is a better solution:
>
> void foo2(T: ParameterTypeTuple!foo[0])(T t){foo(t);}
>
> Then it is just a matter of applying proper value range propagation for  
> IFTY:
>
> void bar(T: short)(T t){...}
>
> void main(){
>      bar(1); // ok
> }

The issue with all this is, IFTI doesn't work that way:

void foo(T: short)(T t) {}

void main()
{
    foo(1);
}

testifti.d(5): Error: template testifti.foo(T : short) does not match any  
function template declaration
testifti.d(5): Error: template testifti.foo(T : short) cannot deduce  
template function from argument types !()(int)

IFTI decides the types of literals before trying to find a proper template  
to instantiate.  Any possibility to intercept the decision of literal type  
or of instantiation would be useful.  I think that it's better suited to  
the constraints, because there is more power there.  But I'm not sure.  If  
you can find a more straightforward way, I'm all for it.

In any case, I need to update the bug report, because the general case is  
if foo has an overload.  For instance:

foo(short s);
foo(wstring w);

foo2 should be able to call both with 1 and "hello" without issue.

My driving use case to create the enhancement was creating a wrapper type  
that intercepted function calls.  I planned to use opDispatch, but it  
didn't quite work with literals.

>> I think the bloat is a wash. Every time I use an array literal, there is
>> a complete instantiation of what would be in an array template inline in
>> the calling function.
>
> With the template function you have that too because the calling  
> function builds the arguments. It is just that you also get the template  
> bloat and an additional function call. Unless it is inlined, of course.

You are right, I hadn't thought of that.  But what about this:  most array  
literals are actually literals (made up of CTFE-decided values).  Making  
them ROM-stored would *eliminate* bloat as compared to the current  
implementation.

Also, given how template-centric D and phobos are, I think at some point  
we need to examine how to minimize template bloat in general, by  
coalescing identical code into one function, or not emitting functions  
that are always inlined, not to mention avoiding storing templates only  
used at compile-time in the code (e.g. isInputRange).

>> Yes, it breaks code. It's worth it. To avoid a heap allocation for [1,
>> 2, 3] is worth breaking code that uses runtime data in an array literal.
>> The compiler can be helpful in the error message:
>>
>> Error somefile.d(345): array literals cannot contain runtime data. Maybe
>> you meant:
>> array(new Foo, new Bar, new Qux);
>>
>> I want to point out that currently there is *NO* way to make an
>> immutable array literal that lives in ROM. This is unacceptable.
>>
>
> There is:
>
> void main(){
>      static immutable x = [1,2,3];
> }

Seems rather odd you should have to jump through these hoops to get the  
compiler to use ROM space.  But I concede that I did not know of this  
trick.  It does not sway my opinion that CTFE should produce ROM-stored  
references, and library function should be used for runtime-constructed  
references.

-Steve


More information about the Digitalmars-d-learn mailing list