Thread overview
unimplemented abstract function compiles.
Aug 11, 2018
Eric
Aug 11, 2018
rikki cattermole
Aug 11, 2018
ag0aep6g
Aug 11, 2018
rikki cattermole
Aug 12, 2018
Nicholas Wilson
Aug 11, 2018
ag0aep6g
Aug 12, 2018
Eric
Aug 12, 2018
ag0aep6g
Aug 13, 2018
Jacob Carlborg
August 11, 2018
Code below compiles while I would not expect it to compile.
Is there a reason that this compiles?

Specs are a bit lite on abstract classes.
Only thing I found that would need to allow this is: "19.4 functions without bodies" https://dlang.org/spec/function.html#function-declarations
But that's explicitly without the abstract keyword..


class I {
  abstract void f();
}

class C : I {
}

unittest {
  C c = cast(C) Object.factory("C");
  c.f();
}


I also couldn't find anything about this in the issue tracker.


August 12, 2018
On 12/08/2018 8:55 AM, Eric wrote:
> Code below compiles while I would not expect it to compile.
> Is there a reason that this compiles?
> 
> Specs are a bit lite on abstract classes.
> Only thing I found that would need to allow this is: "19.4 functions without bodies" https://dlang.org/spec/function.html#function-declarations
> But that's explicitly without the abstract keyword..
> 
> 
> class I {
>    abstract void f();
> }
> 
> class C : I {
> }
> 
> unittest {
>    C c = cast(C) Object.factory("C");
>    c.f();
> }
> 
> 
> I also couldn't find anything about this in the issue tracker.

No bug. You forgot to throw -unittest when you compiled.

/tmp/dmd_run2hTpLG(_D4core7runtime18runModuleUnitTestsUZ19unittestSegvHandlerUNbiPSQCk3sys5posix6signal9siginfo_tPvZv+0x38)[0x55d9bba94a40]
/lib/x86_64-linux-gnu/libpthread.so.0(+0x13150)[0x7f970a253150]
/tmp/dmd_run2hTpLG(_D9onlineapp17__unittest_L14_C1FZv+0x3a)[0x55d9bba8fe6e]
/tmp/dmd_run2hTpLG(_D9onlineapp9__modtestFZv+0x9)[0x55d9bba8fe81]
/tmp/dmd_run2hTpLG(_D4core7runtime18runModuleUnitTestsUZ14__foreachbody2MFPS6object10ModuleInfoZi+0x38)[0x55d9bba94a94]
/tmp/dmd_run2hTpLG(_D6object10ModuleInfo7opApplyFMDFPSQBhQBdZiZ9__lambda2MFyPSQCfQCbZi+0x23)[0x55d9bba9098f]
/tmp/dmd_run2hTpLG(_D2rt5minfo17moduleinfos_applyFMDFyPS6object10ModuleInfoZiZ14__foreachbody2MFKSQCz19sections_elf_shared3DSOZi+0x56)[0x55d9bba921ae]
/tmp/dmd_run2hTpLG(_D2rt19sections_elf_shared3DSO7opApplyFMDFKSQBqQBqQyZiZi+0x45)[0x55d9bba9223d]
/tmp/dmd_run2hTpLG(_D2rt5minfo17moduleinfos_applyFMDFyPS6object10ModuleInfoZiZi+0x22)[0x55d9bba9213a]
/tmp/dmd_run2hTpLG(_D6object10ModuleInfo7opApplyFMDFPSQBhQBdZiZi+0x22)[0x55d9bba90966]
/tmp/dmd_run2hTpLG(runModuleUnitTests+0x13e)[0x55d9bba9486a]
/tmp/dmd_run2hTpLG(_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZv+0x25)[0x55d9bba90f31]
/tmp/dmd_run2hTpLG(_D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ7tryExecMFMDFZvZv+0x20)[0x55d9bba90eb8]
/tmp/dmd_run2hTpLG(_d_run_main+0x1cf)[0x55d9bba90e23]
/tmp/dmd_run2hTpLG(main+0x22)[0x55d9bba8fea6]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7f9709a621c1]
/tmp/dmd_run2hTpLG(_start+0x2a)[0x55d9bba8fd4a]
Error: program killed by signal 11

August 12, 2018
On 08/11/2018 11:20 PM, rikki cattermole wrote:
> On 12/08/2018 8:55 AM, Eric wrote:
>> Code below compiles while I would not expect it to compile.
>> Is there a reason that this compiles?
[...]
> No bug. You forgot to throw -unittest when you compiled.
[...]
> Error: program killed by signal 11

If that's DMD segfaulting, that's definitely a bug.

If that's not DMD segfaulting, you're running the program which Eric expects not to compile. So there might be a bug (or Eric's expectation is wrong).
August 12, 2018
On 08/11/2018 10:55 PM, Eric wrote:
> Code below compiles while I would not expect it to compile.
> Is there a reason that this compiles?
> 
[...]
> 
> class I {
>    abstract void f();
> }
> 
> class C : I {
> }
> 
> unittest {
>    C c = cast(C) Object.factory("C");
>    c.f();
> }

Not a bug, as far as I see.

You don't get compile-time errors with Object.factory. It works at run time, on dynamic values (e.g., a class name entered on the command line). You're calling it with a constant string, but the compiler doesn't care about that. There's no special handling for that.

Object.factory returns `null` when it can't create the object. And it does that in your example, because of the abstract method (and because "C" is wrong; the name must be fully qualified). You're supposed to check for `null` before attempting to use the object.

If you want a compile-time check, don't use Object.factory. Use `new` instead:

    C c = new C; /* Error: cannot create instance of abstract class C */
August 12, 2018
On 12/08/2018 11:12 AM, ag0aep6g wrote:
> On 08/11/2018 11:20 PM, rikki cattermole wrote:
>> On 12/08/2018 8:55 AM, Eric wrote:
>>> Code below compiles while I would not expect it to compile.
>>> Is there a reason that this compiles?
> [...]
>> No bug. You forgot to throw -unittest when you compiled.
> [...]
>> Error: program killed by signal 11
> 
> If that's DMD segfaulting, that's definitely a bug.
> 
> If that's not DMD segfaulting, you're running the program which Eric expects not to compile. So there might be a bug (or Eric's expectation is wrong).

Eric's is wrong. abstract on a method essentially acts as extern except for class methods. Because we're dealing with classes, it won't be hit until runtime. Its weird, but at each stage it all kinda makes sense.
August 12, 2018
On Saturday, 11 August 2018 at 23:12:43 UTC, ag0aep6g wrote:
> On 08/11/2018 11:20 PM, rikki cattermole wrote:
>> On 12/08/2018 8:55 AM, Eric wrote:
>>> Code below compiles while I would not expect it to compile.
>>> Is there a reason that this compiles?
> [...]
>> No bug. You forgot to throw -unittest when you compiled.
> [...]
>> Error: program killed by signal 11
>
> If that's DMD segfaulting, that's definitely a bug.

its not

>/tmp/dmd_run2hTpLG(_D9onlineapp17__unittest_L14_C1FZv+0x3a)[0x55d9bba8fe6e]
>/tmp/dmd_run2hTpLG(_D9onlineapp9__modtestFZv+0x9)[0x55d9bba8fe81]

That is run.dlang.io running the resulting executable. (I know because I've looked a way too many dmd segfaults ;) )

> If that's not DMD segfaulting, you're running the program which Eric expects not to compile. So there might be a bug (or Eric's expectation is wrong).


August 12, 2018
I thought it would work the same way as an interface (which must be implemented by the direct sub class, otherwise compile error).
But apparently it's possible to implement an abstract function anywhere in the class hierarchy. That makes it, in this case, impossible to check during compile time.

I ran into this while loading objects from a file using Object.factory and forgot to implement an abstract function in one class.

August 12, 2018
On 08/12/2018 07:29 PM, Eric wrote:
> I thought it would work the same way as an interface (which must be implemented by the direct sub class, otherwise compile error).

From the spec text [1], I'd also expect an error. It says: "An abstract member function must be overridden by a derived class." And later: "Classes become abstract if any of its virtual member functions are *declared* abstract or if they are defined within an abstract attribute" (emphasis mine).

So, arguably, DMD shouldn't accept your `C` class as abstract. It's not marked as abstract itself, and it doesn't have a declaration of an abstract method either.

But in cases like this it's more likely that the spec will be changed to reflect what DMD does than DMD getting changed to break existing code.

> But apparently it's possible to implement an abstract function anywhere in the class hierarchy. That makes it, in this case, impossible to check during compile time.

You can check it at compile time. The compiler just doesn't do it for you proactively. `C` is an abstract class, just like `I` is, and there's a trait for that [2]:

    static assert(!__traits(isAbstractClass, C));

For nicer syntax, there's also a wrapper around it in std.traits [3]:

    import std.traits: isAbstractClass;
    static assert(!isAbstractClass!C);

If you want to make sure that your class isn't accidentally abstract, you can add an assert like that.


[1] https://dlang.org/spec/attribute.html#abstract
[2] https://dlang.org/spec/traits.html#isAbstractClass
[3] https://dlang.org/phobos/std_traits.html#isAbstractClass
August 13, 2018
On 2018-08-12 19:29, Eric wrote:
> I thought it would work the same way as an interface (which must be implemented by the direct sub class, otherwise compile error).
> But apparently it's possible to implement an abstract function anywhere in the class hierarchy. That makes it, in this case, impossible to check during compile time.

It will be checked during compile time if the class is referenced during compile time. In your example it's not.

The thing is that a library can provide an abstract class which is then subclassed and completely implemented by the user of the library. In that case you don't want an compile error when you're compiling the library but now using the class. One can argue that an abstract class always should be marked with "abstract" but that's not currently how the language is working.

-- 
/Jacob Carlborg