Thread overview
September 03

Hi all.
I'm currently doing OpenGL type binding, and I noticed one assumption in the @safe code.
@safe, scope work only with explicit reference types, if your structure does not have such fields, then checks do not work.
In opengl, and in other APIs, we work with resources not through memory references, but through an integer descriptor.

@safe:

struct BufferView {
  GLuint id;

  //...

  void destroy() @trusted scope {
    //...
  }
}

void doSmthScope(scope BufferRef buf) {...}
void doSmthGlobal(BufferRef buf) {...}

void test() {
  scope BufferView buffer = createBuffer();
  //we also could use generic RAII Handle type with view method
  scope(exit) buffer.dispose();

  doSmth1(buffer); //ok
  doSmth2(buffer); //ok????
}

A hack that can solve the problem is this:

struct BufferView {
  union {
    protected uint _id;
    protected void* _hack;
  }

  auto id() @trusted => _id;
}

There are two problems here:

  1. This is a stinky hack.
  2. We have an unnecessary memory overhead. 2 bytes became 4 bytes.

I suggest this solution:

import core.attributes: saferes; //SAFE RESource

@saferes
struct BufferView {
  protected uint id;
}

All checks for live, scope, safe, etc. must consider structures marked with core.attributes.saferes as a memory reference.

I don't understand the internals of the compiler, so I can't implement it myself. I just saw that there are few occurrences of hasPointers, so this function is not that difficult to implement.
It also doesn't break backwards compatibility.
You can only add @saferes at the parser level.
I don't think that DIP is needed for this function, if it is needed, then I can't write it myself, because I'm bad with English.

What do you think about this?

September 04
On 04/09/2023 2:15 AM, Menshikov wrote:
> We have an unnecessary memory overhead. 2 bytes became 4 bytes.

A uint is 4 bytes. So it would be 4 to 8 on 32bits. On 64bit that would be 16 bytes due to alignment.

But it doesn't have to be wasteful.

```d
union {
	uint id;
	void* ptr;
}
```

September 03

On Sunday, 3 September 2023 at 14:19:55 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

On 04/09/2023 2:15 AM, Menshikov wrote:

>

We have an unnecessary memory overhead. 2 bytes became 4 bytes.

A uint is 4 bytes. So it would be 4 to 8 on 32bits. On 64bit that would be 16 bytes due to alignment.

But it doesn't have to be wasteful.

union {
	uint id;
	void* ptr;
}

ABI doesn't work that way, alignment happens over a larger field in the structure.

struct SafeRef {uint id;}

struct PtrHack {
    union {
    	uint id;
        void* ptr;
    }
}

pragma(msg, SafeRef.sizeof); // 4
pragma(msg, PtrHack.sizeof); // 8

void main(){}
September 04
On 04/09/2023 2:42 AM, Menshikov wrote:
> ABI doesn't work that way, alignment happens over a larger field in the structure.

Yeah that's fine.

In practice you're not saving anything by going below the size of a pointer and even if that wasn't a concern it'll align if you add more fields.

Memory allocators can't allocate memory below the size of a pointer due to needing data structures like freelists.
September 03

On Sunday, 3 September 2023 at 14:15:42 UTC, Menshikov wrote:

>

Hi all.
I'm currently doing OpenGL type binding, and I noticed one assumption in the @safe code.
@safe, scope work only with explicit reference types, if your structure does not have such fields, then checks do not work.
In opengl, and in other APIs, we work with resources not through memory references, but through an integer descriptor.

@safe:

struct BufferView {
  GLuint id;

  //...

  void destroy() @trusted scope {
    //...
  }
}

This is one of the problems DIP 1035's @system variables were designed to solve:

struct BufferView {
    @system GLuint id

    //...
}

When compiling with -preview=systemVariables, Variables with the @system cannot be accessed from @safe code, and are protected against accidental corruption via aliasing, unions, and so on (like how built-in reference types are protected).