Jump to page: 1 24  
Page
Thread overview
#dbugfix 17592
Mar 20, 2018
12345swordy
Mar 21, 2018
bauss
Mar 21, 2018
Mike Parker
Mar 21, 2018
Mike Franklin
Mar 21, 2018
Simen Kjærås
Mar 21, 2018
12345swordy
Mar 21, 2018
Adam D. Ruppe
Mar 21, 2018
12345swordy
Mar 22, 2018
Adam D. Ruppe
Mar 22, 2018
12345swordy
Mar 22, 2018
Adam D. Ruppe
Mar 22, 2018
12345swordy
Mar 22, 2018
Adam D. Ruppe
Mar 22, 2018
12345swordy
Mar 22, 2018
Basile B.
Mar 22, 2018
12345swordy
Mar 22, 2018
Adam D Ruppe
Mar 22, 2018
12345swordy
Mar 22, 2018
Simen Kjærås
Mar 22, 2018
jmh530
Mar 22, 2018
Basile B.
Mar 23, 2018
jmh530
Mar 23, 2018
12345swordy
Mar 23, 2018
Jonathan M Davis
Mar 23, 2018
jmh530
Mar 23, 2018
Jonathan M Davis
Mar 23, 2018
12345swordy
Mar 23, 2018
H. S. Teoh
Mar 23, 2018
Jonathan M Davis
Mar 23, 2018
H. S. Teoh
Mar 21, 2018
Adam D. Ruppe
Mar 22, 2018
SimonN
Mar 22, 2018
Adam D. Ruppe
Mar 22, 2018
Basile B.
March 20, 2018
This is very important to me as I am very interested in using the language for game development.

Yes I know that it's marked as "Duplicated", but I strongly disagree as it is different enough to consider is own issue.

Alex
March 21, 2018
On Tuesday, 20 March 2018 at 21:27:53 UTC, 12345swordy wrote:
> This is very important to me as I am very interested in using the language for game development.
>
> Yes I know that it's marked as "Duplicated", but I strongly disagree as it is different enough to consider is own issue.
>
> Alex

The game development part seems to be irrelevant, because D has been used to make plenty of games.

It is however relevant to how your game would be developed, but your post make it seem like D can't be used to develop games at all.
March 21, 2018
On Tuesday, 20 March 2018 at 21:27:53 UTC, 12345swordy wrote:
> This is very important to me as I am very interested in using the language for game development.
>
> Yes I know that it's marked as "Duplicated", but I strongly disagree as it is different enough to consider is own issue.
>
> Alex

Noted.
March 21, 2018
On Tuesday, 20 March 2018 at 21:27:53 UTC, 12345swordy wrote:
> This is very important to me as I am very interested in using the language for game development.
>
> Yes I know that it's marked as "Duplicated", but I strongly disagree as it is different enough to consider is own issue.
>
> Alex

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

Yeah that's pretty poopy.  I looked into this and I think this is fixable.  Here's my proof of concept:

-----------------
import core.stdc.stdio;
import std.traits;

/*******************************************************************
* This code was copied right out of druntime and
* attributed with @nogc
*******************************************************************/
extern (C) void rt_finalize(void *data, bool det=true) @nogc;
void destroy(T)(T obj) @nogc if (is(T == class))
{
    rt_finalize(cast(void*)obj);
}

/*******************************************************************
* This emplace implementation below was copied
* right out of std.conv and attributed with @nogc
*******************************************************************/
@nogc pure nothrow @safe
void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName)
{
    assert(chunk.length >= typeSize, "emplace: Chunk size too small.");
    assert((cast(size_t) chunk.ptr) % typeAlignment == 0, "emplace: Chunk is not aligned.");
}

T emplace(T, Args...)(T chunk, auto ref Args args) @nogc
	if (is(T == class))
{
    static assert(!isAbstractClass!T, T.stringof ~
        " is abstract and it can't be emplaced");

    // Initialize the object in its pre-ctor state
    enum classSize = __traits(classInstanceSize, T);
    (() @trusted => (cast(void*) chunk)[0 .. classSize] = typeid(T).initializer[])();

    static if (isInnerClass!T)
    {
        static assert(Args.length > 0,
            "Initializing an inner class requires a pointer to the outer class");
        static assert(is(Args[0] : typeof(T.outer)),
            "The first argument must be a pointer to the outer class");

        chunk.outer = args[0];
        alias args1 = args[1..$];
    }
    else alias args1 = args;

    // Call the ctor if any
    static if (is(typeof(chunk.__ctor(args1))))
    {
        // T defines a genuine constructor accepting args
        // Go the classic route: write .init first, then call ctor
        chunk.__ctor(args1);
    }
    else
    {
        static assert(args1.length == 0 && !is(typeof(&T.__ctor)),
            "Don't know how to initialize an object of type "
            ~ T.stringof ~ " with arguments " ~ typeof(args1).stringof);
    }
    return chunk;
}

T emplace(T, Args...)(void[] chunk, auto ref Args args) @nogc
	if (is(T == class))
{
    enum classSize = __traits(classInstanceSize, T);
    testEmplaceChunk(chunk, classSize, classInstanceAlignment!T, T.stringof);
    return emplace!T(cast(T)(chunk.ptr), args);
}

/*******************************************************************
* This code was copied from https://wiki.dlang.org/Memory_Management#Explicit_Class_Instance_Allocation
* and attributed with @nogc
*******************************************************************/
class TestClass
{
    int x;

    this(int x) @nogc
    {
        puts("TestClass's constructor called");
        this.x = x;
    }

    ~this() @nogc
    {
        puts("TestClass's destructor called");
    }
}

T heapAllocate(T, Args...) (Args args) @nogc
{
    import core.stdc.stdlib : malloc;
    import core.memory : GC;

    // get class size of class instance in bytes
    auto size = __traits(classInstanceSize, T);

    // allocate memory for the object
    auto memory = malloc(size)[0..size];
    if(!memory)
    {
        import core.exception : onOutOfMemoryError;
        onOutOfMemoryError();
    }

    puts("Memory allocated");

    // notify garbage collector that it should scan this memory
    GC.addRange(memory.ptr, size);

    // call T's constructor and emplace instance on
    // newly allocated memory
    return emplace!(T, Args)(memory, args);
}

void heapDeallocate(T)(T obj) @nogc
{
    import core.stdc.stdlib : free;
    import core.memory : GC;

    // calls obj's destructor
    destroy(obj);

    // garbage collector should no longer scan this memory
    GC.removeRange(cast(void*)obj);

    // free memory occupied by object
    free(cast(void*)obj);

    puts("Memory deallocated");
}

void main() @nogc
{
    // allocate new instance of TestClass on the heap
    auto test = heapAllocate!TestClass(42);
    scope(exit)
    {
        heapDeallocate(test);
    }

    printf("test.x = %d\n", test.x);
}
-------------

Step 1.  Make `emplace` @nogc
So we need to attribute `std.conv.emplace` as @nogc.  Based on the code above, that looks feasible.  The difficulty, though will be writing thorough tests for it.

Step 2. Make `destroy` @nogc
`destroy` simply calls `rt_finalize` in the runtime, at least for classes.  I declared it as `@nogc` in the code above, but that's a cheat, though I think `rt_finalize` can be made `@nogc` in the runtime.

I'll try to do step 2.  If someone wants to help with step 1, that would be great.

Mike
March 21, 2018
On Wednesday, 21 March 2018 at 08:49:11 UTC, Mike Franklin wrote:

> I think `rt_finalize` can be made `@nogc` in the runtime.

And this is where you're wrong. Consider this:

class A {
    @nogc ~this() {}
}

class B : A {
    ~this() {}
}

A a = new B();
destroy(a); // Is this @nogc?


Essentially, since @nogc and other qualifiers aren't inherited on dtors, it's impossible to know if destroying an instance of a non-final class is @nogc.

There's one case where you can: final classes where no superclass and no member defines a non-@nogc destructor.

In order for this to be done in the general case, dtors need to inherit their qualifiers somehow. That's at the very least a DIP, and any chosen path is highly likely to break existing code.

--
  Simen
March 21, 2018
On Wednesday, 21 March 2018 at 11:13:41 UTC, Simen Kjærås wrote:
> On Wednesday, 21 March 2018 at 08:49:11 UTC, Mike Franklin wrote:
>
>> I think `rt_finalize` can be made `@nogc` in the runtime.
>
> And this is where you're wrong. Consider this:
>
> class A {
>     @nogc ~this() {}
> }
>
> class B : A {
>     ~this() {}
> }
>
> A a = new B();
> destroy(a); // Is this @nogc?
>
>
> Essentially, since @nogc and other qualifiers aren't inherited on dtors, it's impossible to know if destroying an instance of a non-final class is @nogc.
>
> There's one case where you can: final classes where no superclass and no member defines a non-@nogc destructor.
>
> In order for this to be done in the general case, dtors need to inherit their qualifiers somehow. That's at the very least a DIP, and any chosen path is highly likely to break existing code.
>
> --
>   Simen

You can simply check the .dtor symbols at compile time to see if every .dtor symbol from child to root have a .dtor that have the @nogc attribute(and other attributes as well). If it does, add that attribute to destroy.
March 21, 2018
On Wednesday, 21 March 2018 at 08:49:11 UTC, Mike Franklin wrote:
> Step 1.  Make `emplace` @nogc
> So we need to attribute `std.conv.emplace` as @nogc.

No, do not do that! `emplace` is ALREADY `@nogc` when given appropriate arguments. Adding the explicit annotation will limit its flexibility without enabling any new uses.

This compiles right now:

---
import std.conv;

class Foo {
        this() @nogc {}
}

@nogc void main() {
        char[__traits(classInstanceSize, Foo)] buffer;
        emplace!Foo(buffer[]);
}
---


`emplace` is `@nogc` if the constructor it calls is `@nogc`.



The real bug with nogc and things like emplace are the error messages. They should tell you the item in the call chain where @nogc is NOT inferred. So if the ctor there was not annotated right now it says:

 Error: @nogc function D main cannot call non-@nogc function std.conv.emplace!(Foo).emplace

Well, it should say:

 Error: @nogc function D main cannot call non-@nogc function std.conv.emplace!(Foo).emplace
   >>  emplace was not inferred because it calls non-@nogc function `Foo.this()`


That supplemental bit with the >> indicates the ACTUAL reason nogc failed. Change that and the rest of the call tree becomes nogc automatically.

I seem to remember filing this to bugzilla sometime but bugzilla search is so unbelievably bad I can never find anything on there so idk for sure.

But this error message change would be my #1 request for nogc. It'd keep others from chasing false leads.


> Step 2. Make `destroy` @nogc
> `destroy` simply calls `rt_finalize` in the runtime, at least for classes.  I declared it as `@nogc` in the code above, but that's a cheat, though I think `rt_finalize` can be made `@nogc` in the runtime.

This cannot be proven at compile time since it calls destructors based on a dynamic type which may or may not keep the nogc promise.

Of course, dtors that gc allocate are usually broken anyway... but it isn't a compile time guarantee in the current language and a library change cannot fix that alone.
March 21, 2018
On Wednesday, 21 March 2018 at 13:39:28 UTC, 12345swordy wrote:
> You can simply check the .dtor symbols at compile time to see if every .dtor symbol from child to root have a .dtor that have the @nogc attribute

In Simen's example, the child information is not available at compile time. This line here:

A a = new B();

discards the static type. The compiler could probably cheat and figure it out anyway in this example, but suppose:

---
class A {
     @nogc ~this() {}
 }

class B : A {
    ~this() {}
}

class C : A {
    @nogc ~this() {}
}


A build(string name) {
   if(name == "B") return new B();
   else return new C();
}

void main() {
    A a = build(readln());
    destroy(a);
}
---


This is very clearly a runtime decision: whether it is B or C is determined by user input. But B's dtor is not @nogc... and there's thus no way to tell for sure if destroy(a) is or not, since it will call the child class based on the runtime decision.

so it is impossible for the compiler to know which child class is actually called until runtime... too late for a compile-time nogc check.
March 21, 2018
On Wednesday, 21 March 2018 at 14:04:58 UTC, Adam D. Ruppe wrote:
> On Wednesday, 21 March 2018 at 13:39:28 UTC, 12345swordy wrote:
>> You can simply check the .dtor symbols at compile time to see if every .dtor symbol from child to root have a .dtor that have the @nogc attribute
>
> In Simen's example, the child information is not available at compile time. This line here:
>
> A a = new B();
>
> discards the static type. The compiler could probably cheat and figure it out anyway in this example, but suppose:
>
> ---
> class A {
>      @nogc ~this() {}
>  }
>
> class B : A {
>     ~this() {}
> }
>
> class C : A {
>     @nogc ~this() {}
> }
>
>
> A build(string name) {
>    if(name == "B") return new B();
>    else return new C();
> }
>
> void main() {
>     A a = build(readln());
>     destroy(a);
> }
> ---
>
>
> This is very clearly a runtime decision: whether it is B or C is determined by user input. But B's dtor is not @nogc... and there's thus no way to tell for sure if destroy(a) is or not, since it will call the child class based on the runtime decision.
>
> so it is impossible for the compiler to know which child class is actually called until runtime... too late for a compile-time nogc check.

That seems to be it's own separate problem, as it involves generating dynamic types at run-time, which it needs run-time equivalent of attribute checking. My example assumes that the classes created are static types not dynamic types. Besides I do not like implied conversions when it comes classes, as I believe it is a horrible idea.
March 22, 2018
On Wednesday, 21 March 2018 at 19:21:15 UTC, 12345swordy wrote:
> That seems to be it's own separate problem, as it involves generating dynamic types at run-time, which it needs run-time equivalent of attribute checking.

But @nogc is a compile time thing, meaning it cannot work here.

> My example assumes that the classes created are static types not dynamic types. Besides I do not like implied conversions when it comes classes, as I believe it is a horrible idea.

All classes are dynamic types, this is their reason for existing!
« First   ‹ Prev
1 2 3 4