Thread overview
October 12

I have a small program like below. Everything works as it should in classes; even if I call the structure with the new operator. But if I stop using classes, scope doesn't work properly!

class/* STEP2
struct//*/
Foo {
  this(int i) {
    i.writefln!"Object %s is created...";
  }
  ~this() {
  writeln("Object was deleted!");
  }
}

import std.stdio;
void main() {
   write("call ");
   writeln("return ", loop);
}

auto loop() {
  enum func = __FUNCTION__;
  func.writeln;

  for (auto i = 1; i != 3; ++i) {
    scope // STEP1
    auto foo = new Foo(i);
  }
  return func;
}

Please put a comment (//) mark at the beginning of the line that says STEP1 and then STEP2. Then change STEP1 back to its previous state, that is, enable the scope. You will sense that something is wrong...

Why doesn't it work correctly in structs?
Or is scope the default in structs?

SDB@79

October 12

On Saturday, 12 October 2024 at 12:02:04 UTC, Salih Dincer wrote:

>

... even if I call the structure with the new operator. But if I stop using classes, scope doesn't work properly!

Edit: It seems like scope is ineffective in structures. In fact, if there is the new operator, it is as if scope does not exist, and if it is construct without the new operator, it is as if scope does exist; is this normal?

SDB@79

October 12

On Saturday, 12 October 2024 at 12:10:17 UTC, Salih Dincer wrote:

>

On Saturday, 12 October 2024 at 12:02:04 UTC, Salih Dincer wrote:

>

... even if I call the structure with the new operator. But if I stop using classes, scope doesn't work properly!

Declaring a scope SomeClass initialized with new is a special case to allocate on the stack:
https://dlang.org/spec/attribute.html#scope-class-var

>

Edit: It seems like scope is ineffective in structures. In fact, if there is the new operator, it is as if scope does not exist, and if it is construct without the new operator, it is as if scope does exist; is this normal?

If you want stack allocation of structs, why use new?

October 13
You are not wrong, when it is a struct, it is being heap allocated.

Looks like the optimization for classes, hasn't been applied to structs.

https://issues.dlang.org/show_bug.cgi?id=24806
October 12

On Saturday, 12 October 2024 at 13:08:03 UTC, Nick Treleaven wrote:

>

If you want stack allocation of structs, why use new?

Actually, I almost never use the new operator except with(). I was surprised because it seemed inconsistent here and wanted to share my experiment.

On Saturday, 12 October 2024 at 13:11:52 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

You are not wrong, when it is a struct, it is being heap allocated.

Looks like the optimization for classes, hasn't been applied to structs.

https://issues.dlang.org/show_bug.cgi?id=24806

So if scope is a facility for classes, it should give an error when used in structures. Is that so? I understand this from the issue you opened.

SDB@79

October 13
On 13/10/2024 3:12 AM, Salih Dincer wrote:
>     You are not wrong, when it is a struct, it is being heap allocated.
> 
>     Looks like the optimization for classes, hasn't been applied to structs.
> 
>     https://issues.dlang.org/show_bug.cgi?id=24806 <https://
>     issues.dlang.org/show_bug.cgi?id=24806>
> 
> So if |scope| is a facility for classes, it should give an error when used in structures. Is that so? I understand this from the issue you opened.

``scope`` offers protection from escaping.

It is functioning correctly.

The problem is an optimization you want, isn't being applied by the frontend, but is elsewhere.
October 13

On Saturday, 12 October 2024 at 13:11:52 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

You are not wrong, when it is a struct, it is being heap allocated.

Sorry for prolonging the topic. I am very curious about your answers along with your patience...

Can we say that structs are in the stack (LIFO) as long as we do not use the new operator? Also, should using scope in structures cause a change? I never seen it does! However, there is no incompatibility here: Whether it is a class or a struct, when you use the new operator, the first run constructor becomes the first run destructor with FIFO logic.

You can reverse this situation with scope (but only in classes). By reverse I don't mean LIFO! In fact, the correct expression is that when you are done with the object, it is removed.

Thanks, SDB@79

October 13

On Sunday, 13 October 2024 at 05:12:32 UTC, Salih Dincer wrote:

>

Can we say that structs are in the stack (LIFO) as long as we do not use the new operator?

Just to note that new does not give you a struct, it gives a struct pointer. Structs use the stack when declared inside a stack-allocated function frame. A struct B field inside another struct A will use A's storage. A could be allocated on the heap with new.

>

Also, should using scope in structures cause a change? I never seen it does!

With -dip1000 and @safe, scope is meaningful for a struct - it applies to the fields of a struct. However, it can be inferred too.

@safe:

struct S
{
    int* i;
}

int* f()
{
    int i;
    scope s = S(&i); // OK (scope will be inferred if missing)
    return s.i; // error
}
>

However, there is no incompatibility here: Whether it is a class or a struct, when you use the new operator, the first run constructor becomes the first run destructor with FIFO logic.

I don't think so for new:

"Important: The order in which the garbage collector calls destructors for unreferenced objects is not specified."

From https://dlang.org/spec/class.html#destructors

October 18
On 13/10/2024 6:12 PM, Salih Dincer wrote:
> On Saturday, 12 October 2024 at 13:11:52 UTC, Richard (Rikki) Andrew Cattermole wrote:
>> You are not wrong, when it is a struct, it is being heap allocated.
> 
> Sorry for prolonging the topic. I am very curious about your answers along with your patience...
> 
> Can we say that structs are in the stack (LIFO) as long as we do not use the new operator? Also, should using scope in structures cause a change? I never seen it does! However, there is no incompatibility here: Whether it is a class or a struct, when you use the new operator, the first run constructor becomes the first run destructor with FIFO logic.
> 
> You can reverse this situation with scope (but only in classes). By reverse I don't mean LIFO! In fact, the correct expression is that when you are done with the object, it is removed.
> 
> Thanks, SDB@79

Sorry for not replying sooner, COVID has not been a fun virus for my mind to have.

When a variable is declared with a struct that needs cleanup, it effectively rewrites the following statements into a try finally:

```d
void main()
{
	S s = 0;
	try
	{
		if (true)
			return 0;
	}
	finally
		s.~this();
	return 0;
}
```

There are simplifications of this, without the need for the try finally, but we can ignore it for the purposes of this explanation.

The same can be seen with a class that was allocated on the stack:

```d
void main()
{
	scope C c = new C;
	try
	{
		if (true)
			return 0;
	}
	finally
		delete c;
	return 0;
}
```

Nested:

```d
void main()
{
	scope C c = new C;
	try
	{
		S s = 0;
		try
		{
			if (true)
				return 0;
		}
		finally
			s.~this();
	}
	finally
		delete c; // aggregate dtor is called
	return 0;
}
```

How nesting destroys fields:

```d
class C : Object
{
	S s;
	scope ~this() // user dtor
	{
	}
	scope ~this() // field dtor
	{
		this.s.~this();
	}
	scope ~this() // aggregate dtor
	{
		// user dtor , field dtor
		this.~this() , this.~this();
	}
}
```

This should cover the ordering on the stack. If you have any further questions about it please ask.

---------------------------------------------

> Also, should using scope in structures cause a change?
> I never seen it does!

The scope attribute in a declaration body such as a struct, class or union applies to the method, for its this pointer. It does not apply to the fields.

Generally speaking, scope should not be on a type or field declaration.

Good:

```d
struct S {
	int fields;

	private shared int* thing;

export @safe nothrow @nogc:

	this() scope {}

	void method1() scope {}

	scope { // ok
		void method2() {}
	}

scope: // no indication at function that it is applied, not great
	void method3() {}
}
```

Bad:

```d
struct S {
export @safe nothrow @nogc scope:

	int fields;

	this() {}

	void method() {}
}

shared scope struct S {
export @safe nothrow @nogc:

	int fields;

	this() {}

	void method() {}
}
```

October 19

On Friday, 18 October 2024 at 07:43:47 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Sorry for not replying sooner, COVID has not been a fun virus for my mind to have.

When a variable is declared with a struct that needs cleanup, it effectively rewrites the following statements into a try finally:

void main()
{
	S s = 0;
	try
	{
		if (true)
			return 0;
	}
	finally
		s.~this();
	return 0;
}

Thank you for your answer. I also had a minor operation and I am fine now. I hope you are fine too.

SDB@79