Thread overview
Run-time setting of immutable variable?
Sep 02
DLearner
Sep 02
DLearner
Sep 02
Kagamin
Sep 02
jfondren
Sep 03
DLearner
September 02

Suppose there is a variable that is set once per run, and is (supposed) never to be altered again. However, the value to which it is set is not known at compile time.
Example below, variable is 'ArrPtr';

ubyte[10] Arr;

// immutable void* ArrPtr;
void* ArrPtr;

void main() {

   ArrPtr = cast(void*)Arr[0];

// <Lots of code depending on ArrPtr, but not supposed to modify ArrPtr>

}

Is there a way of getting D to guarantee that ArrPtr is never modified after

   ArrPtr = cast(void*)Arr[0];

Best regards

September 02
On Thu, Sep 02, 2021 at 04:01:19PM +0000, DLearner via Digitalmars-d-learn wrote:
> Suppose there is a variable that is set once per run, and is (supposed) never to be altered again.  However, the value to which it is set is not known at compile time.

This is the classic use case of `immutable`.  Using the example you gave, you'd move the initialization of ArrPtr into a static module constructor:

	immutable void* ArrPtr;
	shared static this() {
		ArrPtr = ...; // initialize it here
	}

	void main() {
		... // ArrPtr is immutable from here on.
	}


T

-- 
Дерево держится корнями, а человек - друзьями.
September 02

On 9/2/21 12:01 PM, DLearner wrote:

>

Suppose there is a variable that is set once per run, and is (supposed) never to be altered again.  However, the value to which it is set is not known at compile time.
Example below, variable is 'ArrPtr';

ubyte[10] Arr;

// immutable void* ArrPtr;
void* ArrPtr;

void main() {

    ArrPtr = cast(void*)Arr[0];

// <Lots of code depending on ArrPtr, but not supposed to modify ArrPtr>

}

Is there a way of getting D to guarantee that ArrPtr is never modified after

    ArrPtr = cast(void*)Arr[0];
```]

You shouldn't be doing this. Arr is not immutable, so ArrPtr shouldn't point at it if it's immutable.

If you want to guarantee that ArrPtr never changes once set, yet still want it to point at mutable data, you need to use a type that does that (like a head mutable or "write once" type), which I believe doesn't exist in phobos.

If you don't actually need to mutate the data via ArrPtr, you can make it const, and use H.S. Teoh's solution. But you should not use immutable, as the compiler implies that data pointed at is also immutable (and of course, you won't need casting). Make sure you use regular static this, not shared static this, as your fields are thread-local, not shared.

-Steve

September 02

On Thursday, 2 September 2021 at 16:46:46 UTC, Steven Schveighoffer wrote:

>

On 9/2/21 12:01 PM, DLearner wrote:

>

Suppose there is a variable that is set once per run, and is (supposed) never to be altered again.  However, the value to which it is set is not known at compile time.
Example below, variable is 'ArrPtr';

ubyte[10] Arr;

// immutable void* ArrPtr;
void* ArrPtr;

void main() {

    ArrPtr = cast(void*)Arr[0];

// <Lots of code depending on ArrPtr, but not supposed to modify ArrPtr>

}

Is there a way of getting D to guarantee that ArrPtr is never modified after

    ArrPtr = cast(void*)Arr[0];
```]

You shouldn't be doing this. Arr is not immutable, so ArrPtr shouldn't point at it if it's immutable.

If you want to guarantee that ArrPtr never changes once set, yet still want it to point at mutable data, you need to use a type that does that (like a head mutable or "write once" type), which I believe doesn't exist in phobos.

If you don't actually need to mutate the data via ArrPtr, you can make it const, and use H.S. Teoh's solution. But you should not use immutable, as the compiler implies that data pointed at is also immutable (and of course, you won't need casting). Make sure you use regular static this, not shared static this, as your fields are thread-local, not shared.

-Steve

The following clean-compiled and produced the expected result:

    ubyte[10] Arr;

    immutable void* ArrPtr;
	shared static this() {
		ArrPtr = cast(immutable void*)(&Arr[0]);
	}

	void main() {
	
	  import std.stdio;
      void* ArrPtr2;

      ArrPtr2 = cast(void*)(&Arr[0]);
		
      writeln("ArrPtr = ", ArrPtr);
      writeln("ArrPtr2 = ", ArrPtr2);  // consistency test

//      ArrPtr = ArrPtr + 1;  // mutability test - compile (correctly) failed when uncommented.
      ArrPtr2 = ArrPtr2 + 1;
	
      Arr[1] = 4;
	  Arr[1] = 7;  // mutability test	
	}

The following produced deprecated warnings, but still seemed to work:

    ubyte[10] Arr;

    immutable void* ArrPtr;
	static this() {
		ArrPtr = cast(immutable void*)(&Arr[0]);
	}

	void main() {
	
	  import std.stdio;
      void* ArrPtr2;

      ArrPtr2 = cast(void*)(&Arr[0]);
		
      writeln("ArrPtr = ", ArrPtr);
      writeln("ArrPtr2 = ", ArrPtr2);  // consistency test

//      ArrPtr = ArrPtr + 1;  // mutability test on ArrPtr - compile (correctly) failed when uncommented.
      ArrPtr2 = ArrPtr2 + 1;
	
      Arr[1] = 4;
	  Arr[1] = 7;  // mutability test on Arr
	}

I am looking for a mutable Arr but would like an immutable ArrPtr.

`Arr` is not immutable, so `ArrPtr` shouldn't point at it if it's immutable.

Surely there is no inconsistency - at run time the array is in a fixed place, so ArrPtr is (or at least should be) a constant, but the contents of the array
can vary as the program runs.

September 02

If you want only address, you can keep it as size_t:

ubyte[10] Arr;
immutable size_t Address;
static this() {
	Address = cast(size_t)(&Arr[0]);
}
September 02

On Thursday, 2 September 2021 at 17:17:15 UTC, DLearner wrote:

>

Surely there is no inconsistency - at run time the array is in a fixed place, so ArrPtr is (or at least should be) a constant, but the contents of the array
can vary as the program runs.

In the case of immutable(T)* ArrPtr, the contents of the pointed-to array cannot vary as the program runs. If it's immutable, nothing in the program ever mutates it. In the case of const(T)* ArrPtr, the contents of the pointed-to array can vary as the program runs, and the only restriction is that the array can't be mutated through ArrPtr.

If what you mean is that you want the pointer to never change, T * const ArrPtr in C syntax, but that you don't care if the pointed-to array changes via ArrPtr or any other reference, then I don't think D can express this. You could make ArrPtr a function:

void main() {
    int[] xs = [1, 2, 3, 4];
    int* p() { return &xs[2]; }
    p[0] = 0;
    assert(xs == [1, 2, 0, 4]);
    p++; // Error: ... not an lvalue and cannot be modified
}
September 02
On Thu, Sep 02, 2021 at 05:17:15PM +0000, DLearner via Digitalmars-d-learn wrote: [...]
> The following clean-compiled and produced the expected result:
> ```
>     ubyte[10] Arr;
> 
>     immutable void* ArrPtr;
> 	shared static this() {
> 		ArrPtr = cast(immutable void*)(&Arr[0]);
> 	}

Casting this to immutable is unsafe, because Arr is actually mutable. In D, const and immutable are transitive, so when you cast a pointer into immutable, you're basically promising that *both* the pointer *and* the data pointed to will never change. The compiler is free to assume that the data pointed to will never change, which may cause consistency problems if the data *does* change later.

In cases like this, const is a better choice: const ensures that the pointer will not mutate and the pointed-to data cannot be mutated through this pointer, but the data *may* be mutated through another reference (e.g., by modifying Arr directly).


[...]
> I am looking for a mutable Arr but would like an immutable ArrPtr.

In D, const and immutable are transitive, so you cannot do this (but see
below).


> ```
> `Arr` is not immutable, so `ArrPtr` shouldn't point at it if it's
> immutable.
> ```
> Surely there is no inconsistency - at run time the array is in a fixed
> place,  so ArrPtr is (or at least should be) a constant, but the
> contents of the array can vary as the program runs.

In this case, what you want is const, not immutable.  Const means you cannot modify the pointer, and you cannot mutate the data through this pointer, but you *can* modify the array from a mutable reference.


T

-- 
You are only young once, but you can stay immature indefinitely. -- azephrahel
September 02

On 9/2/21 1:17 PM, DLearner wrote:

>

I am looking for a mutable Arr but would like an immutable ArrPtr.

Then you want const not immutable.

Here is the reason:

void main()
{
    int x = 5;
    immutable int *ptr = cast(immutable int *)&x;
    assert(*ptr == 5); // ok
    x = 6;
    assert(*ptr == 5); // what happens here?
}

Depending on optimizations and compiler constant folding, that second assert may pass.

immutable means "I can never change and everything I point at can never change". The compiler is free to use this knowledge to avoid redoing calculations it has already done. I tknows that *ptr == 5 already, and that can never change, so it just doesn't even bother with the second assert.

However, make ptr const, and now not only do you not need the cast, but the second assert will fail as expected.

>
`Arr` is not immutable, so `ArrPtr` shouldn't point at it if it's immutable.

Surely there is no inconsistency - at run time the array is in a fixed place,  so ArrPtr is (or at least should be) a constant, but the contents of the array
can vary as the program runs.

In D, const and immutable are transitive, which means that you can't have a pointer that is const, but allows changing what it points at. So while a const pointer may work for your purposes, you may want a specialized type, or a property function. It depends on how you intend to use ArrPtr. Others have given good answers to this problem.

-Steve

September 02
On 9/2/21 9:01 AM, DLearner wrote:
> Suppose there is a variable that is set once per run, and is (supposed) never to be altered again.

An accessor function can be a solution, which supports your other comment about data potentially mutating by other means:

// Assume these are in a module
// vvvvvvvvvvvvvvvvvvvvvvvvvvvv

// This is private; you can even name it arr_.
private
ubyte[10] arr;

// This is the accessor:
public
const(ubyte)* arrPtr() {
  return arr.ptr;
}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

void main() {
  *arrPtr = 42;  // Compilation ERROR; good.
}

Ali
September 03

On Thursday, 2 September 2021 at 23:12:28 UTC, Steven Schveighoffer wrote:

>

[...]

>

immutable means "I can never change and everything I point at can never change".
[...]

If that is how the language defines the keyword 'immutable' when used in the definition of a pointer variable, then so be it.

I would, however, suggest that the additional 'action-at-a-distance' implication (freezing not just the variable itself, but also it's target) is inconsistent with the definition of 'immutable' with other variable types.

Surely it would be better to reserve 'immutable' on a pointer to mean simply set-once on the pointer itself (with no implications for whatever the pointer is pointing to), and another keyword ('blocked'?) for the current definition?