Case 1 (binary level, C + D):

----- sample.d -----
module sample;
private int func() { return 42; }
----------

----- oops.c -----
#include <stdio.h>

extern int _D6sample4funcFZi();

void* _Dmodule_ref = 0;
void* _D15TypeInfo_Struct6__vtblZ = 0;

void _Dmain() { }

int main()
{
    long value = _D6sample4funcFZi();
    printf("%ld\n", value);
    return 0;
}
----------

----- shell -----
$ dmd - c sample.d
$ gcc -c oops.c
$ gcc oops.o sample.o
./a.out
42
----------

This is valid code now and will break. Internal linkage is a binary-level encapsulation tool, contrary to language-level private. Not that when compiling sample.d dmd can't know if oops.c will actually link to func and make decision about internal linkage. Saying this is invalid code may severely limit language interconnection possibilities and something like injecting druntime.

Case 2 (pure D, language level):

----- sample.d -----
module sample;
private struct Hest
{
    int a, b;
}
public alias Hidden UseMe;
----------

----- oops.d -----
import sample;
import std.stdio;

void main()
{
    UseMe tmp;
    tmp.a = 42;
}
----------

Now suddenly changing Test definition may break code in oops.d and we need first to make sure Test is not referenced in public aliases or as function parameter or as public type member field etc. Again, this code itself is not only valid but quite idiomatic. Internal linkage provides strong guarantees that any change to symbol will affect only its module, with no special cases.

Case 3 (consistency):

I do insist on keeping class and module protection attribute consistent. Saying class privates are always accessible from other modules via tupleof & friends and module privates may be not is causing confusion for no real gain. Also it may break code if we have any means to enumerate module symbols via traits (do we? I don't know).

Reply via email to