How mutable is immutable?

Don Clugston dac at nospam.com
Mon Oct 22 08:28:17 PDT 2012


On 18/10/12 19:43, Timon Gehr wrote:
> On 10/18/2012 10:08 AM, Don Clugston wrote:
>> On 17/10/12 18:02, Timon Gehr wrote:
>>> On 10/17/2012 01:49 PM, Don Clugston wrote:
>>>> ...
>>>>
>>>> That's the point -- *which* checks are missing from @safe?
>>>
>>> Escaping stack data and arbitrarily freeing memory are not operations
>>> found in memory safe languages.
>>
>> HOW do you propose to check for escaping stack data?
>>
>
> Static escape analysis. Use the 'scope' qualifier to designate
> data that is not allowed to be escaped in order to make it modular.
>
>> ...
>>>
>>> The implementation of the 'scope' storage class should be fixed. We
>>> could then require an unsafe cast(scope) to disable prevention of stack
>>> address escaping.
>>
>> No we can't. f cannot know that the string it has been given is on the
>> stack. So main() must prevent it from being given to f() in the first
>> place. How can it do that?
>>
>
> f can know that it mustn't escape it, which is enough.
>
>> void foo(bool b, string y)
>> {
>>    immutable (char)[4] x = "abba";
>>    string s = b ? x : y;
>>    f(s);
>> }
>>
>> Make it safe.
>>
>
> It is safe if the parameter to f is marked with 'scope'. (and this in
> turn obliges f not to escape it.)

Well, OK, but that involves changing the semantics of immutable. You 
could not pass this kind of "local immutable" to _any_ existing code.
It would render almost all existing code that uses immutable obsolete.

And what do you get in exchange? Practically nothing!

>
> Analyze scope on the expression level.
>
> The analysis would determine that x[] is 'scope'. It would
> conservatively propagate this fact to (b ? x[] : y). Then the local
> variable 's' will get the 'scope' storage class.
>
> In general, use a fixed-point iteration to determine all local
> variables that might refer to scope'd data and prevent that they get
> escaped.
>
>>
>>> Rust's borrowed pointers may give some hints on how
>>> to extend 'scope' to fields of structs.
>>
>> I think it is more fundamental than that.
>>
>>> As to delete, delete is as unsafe when the involved data is immutable
>>> as when it is mutable. Why require an additional cast in one case?
>>
>> This is not about safety.
>> Modifying immutable data breaks the type system. Deleting mutable data
>> does not.
>> AFAIK it is safe to implement delete as a call to the
>> finalizer, followed by setting the memory to T.init.
>> ...
>
>
> Now I see where you are coming from. This is indeed a safe approach for
> references to/arrays of fully mutable value types, but not for delete
> in general.
>
> Make sure to treat void* specially though.
>
> struct S{ immutable int x; this(int x){this.x=x;}}
>
> void main()@safe{
>      void* s = new S(2);
>      delete s;
> }
>
> Class instance memory does not have a T.init, because it is not
> assigned a T. And even if it was, how would you know at compile time if
> the bound instance has any immutable fields?
> Should that be a runtime exception?

Probably. Yeah, it's a bit hard if you have a base class, you can't 
statically check if it has immutable members in a derived class.
Or you could be conservative and disallow delete of anything where you 
don't know the exact type at compile time.




More information about the Digitalmars-d mailing list