Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dmd-cxx] Implement static foreach, aliasing traits, and fix allMembers. #10791

Merged
merged 1 commit into from
Feb 20, 2020

Conversation

ibuclaw
Copy link
Member

@ibuclaw ibuclaw commented Feb 16, 2020

This'll be the notable addition to gdc10, as it's too late to switch to self-hosted now.

Bootstrapped and regression tested on x86_64-linux-gnu. Though we'll wait and see how the dmd autotester responds, particularly on Windows and OSX.

ChangeLog entries below:


Implement DIP 1010 - (Static foreach)

Support for static foreach has been added.

static foreach is a conditional compilation construct that is to foreach what static if is to if.
It is a convenient way to generate declarations and statements by iteration.

import std.conv: to;

static foreach(i; 0 .. 10)
{

    // a `static foreach` body does not introduce a nested scope
    // (similar to `static if`).

    // The following mixin declaration is at module scope:
    mixin(`enum x` ~ to!string(i) ~ ` = i;`); // declares 10 variables x0, x1, ..., x9
}

import std.range: iota;
// all aggregate types that can be iterated with a standard `foreach`
// loop are also supported by static foreach:
static foreach(i; iota(10))
{
    // we access the declarations generated in the first `static foreach`
    pragma(msg, "x", i, ": ", mixin(`x` ~ to!string(i)));
    static assert(mixin(`x` ~ to!string(i)) == i);
}

void main()
{
    import std.conv: text;
    import std.typecons: tuple;
    import std.algorithm: map;
    import std.stdio: writeln;

    // `static foreach` has both declaration and statement forms
    // (similar to `static if`).

    static foreach(x; iota(3).map!(i => tuple(text("x", i), i)))
    {
        // generates three local variables x0, x1 and x2.
        mixin(text(`int `,x[0],` = x[1];`));

        scope(exit) // this is within the scope of `main`
        {
            writeln(mixin(x[0]));
        }
    }

    writeln(x0," ",x1," ",x2); // first runtime output
}

Aliases can be created directly from a __trait.

Aliases can be created directly from the traits that return symbol(s) or tuples.
This includes getMember, allMembers, derivedMembers, parent, getOverloads,
getVirtualFunctions, getVirtualMethods, getUnitTests, getAttributes and finally getAliasThis.
Previously an AliasSeq was necessary in order to alias their return.
Now the grammar allows to write shorter declarations:

struct Foo
{
    static int a;
}

alias oldWay = AliasSeq!(__traits(getMember, Foo, "a"))[0];
alias newWay = __traits(getMember, Foo, "a");

To permit this it was more interesting to include __trait in the basic types
rather than just changing the alias syntax. So additionally, wherever a type appears
a __trait can be used, for example in a variable declaration:

struct Foo { static struct Bar {} }
const(__traits(getMember, Foo, "Bar")) fooBar;
static assert(is(typeof(fooBar) == const(Foo.Bar)));

fix Issue 10100 - Identifiers with double underscores and allMembers

Convert the identifier whitelist into a blacklist of all possible
internal D language declarations.

----

Implement DIP 1010 - (Static foreach)

Support for `static foreach` has been added.

`static foreach` is a conditional compilation construct that is to `foreach` what `static if` is to `if`.
It is a convenient way to generate declarations and statements by iteration.

```
import std.conv: to;

static foreach(i; 0 .. 10)
{

    // a `static foreach` body does not introduce a nested scope
    // (similar to `static if`).

    // The following mixin declaration is at module scope:
    mixin(`enum x` ~ to!string(i) ~ ` = i;`); // declares 10 variables x0, x1, ..., x9
}

import std.range: iota;
// all aggregate types that can be iterated with a standard `foreach`
// loop are also supported by static foreach:
static foreach(i; iota(10))
{
    // we access the declarations generated in the first `static foreach`
    pragma(msg, "x", i, ": ", mixin(`x` ~ to!string(i)));
    static assert(mixin(`x` ~ to!string(i)) == i);
}

void main()
{
    import std.conv: text;
    import std.typecons: tuple;
    import std.algorithm: map;
    import std.stdio: writeln;

    // `static foreach` has both declaration and statement forms
    // (similar to `static if`).

    static foreach(x; iota(3).map!(i => tuple(text("x", i), i)))
    {
        // generates three local variables x0, x1 and x2.
        mixin(text(`int `,x[0],` = x[1];`));

        scope(exit) // this is within the scope of `main`
        {
            writeln(mixin(x[0]));
        }
    }

    writeln(x0," ",x1," ",x2); // first runtime output
}
```

----

Aliases can be created directly from a `__trait`.

Aliases can be created directly from the traits that return symbol(s) or tuples.
This includes `getMember`, `allMembers`, `derivedMembers`, `parent`, `getOverloads`,
`getVirtualFunctions`, `getVirtualMethods`, `getUnitTests`, `getAttributes` and finally `getAliasThis`.
Previously an `AliasSeq` was necessary in order to alias their return.
Now the grammar allows to write shorter declarations:

```
struct Foo
{
    static int a;
}

alias oldWay = AliasSeq!(__traits(getMember, Foo, "a"))[0];
alias newWay = __traits(getMember, Foo, "a");
```

To permit this it was more interesting to include `__trait` in the basic types
rather than just changing the alias syntax. So additionally, wherever a type appears
a `__trait` can be used, for example in a variable declaration:

```
struct Foo { static struct Bar {} }
const(__traits(getMember, Foo, "Bar")) fooBar;
static assert(is(typeof(fooBar) == const(Foo.Bar)));
```

----

fix Issue 10100 - Identifiers with double underscores and allMembers

Convert the identifier whitelist into a blacklist of all possible
internal D language declarations.
@dlang-bot
Copy link
Contributor

Thanks for your pull request, @ibuclaw!

Bugzilla references

Auto-close Bugzilla Severity Description
10100 regression Identifiers with double underscores and allMembers

Testing this PR locally

If you don't have a local development environment setup, you can use Digger to test this PR:

dub fetch digger
dub run digger -- build "dmd-cxx + dmd#10791"

@ibuclaw
Copy link
Member Author

ibuclaw commented Feb 16, 2020

Apparently the win32 box can't checkout the repository anymore... oh well.

Copy link
Member

@WalterBright WalterBright left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really appreciate the thankless job you're doing here - thank you!

@ibuclaw
Copy link
Member Author

ibuclaw commented Feb 16, 2020

I'm sure people can't wait to use static foreach within the dmd compiler implementation. ;-)

For those who are attentive, the magic template functions in the D implementation have been de-templatized in the C++ version. Don't know if it would be desirable to also remove the templates in dmd/master as well.

@UplinkCoder
Copy link
Member

@ibuclaw I would greatly appreciate seeing less templates in dmd.

@ibuclaw ibuclaw merged commit c89afe8 into dlang:dmd-cxx Feb 20, 2020
@ibuclaw ibuclaw deleted the dmd-cxx-staticforeach branch February 20, 2020 21:56
kraj pushed a commit to kraj/gcc that referenced this pull request Mar 13, 2020
1. Implement DIP 1010 - (Static foreach)

Support for 'static foreach' has been added.  'static foreach' is a conditional
compilation construct that is to 'foreach' what 'static if' is to 'if'.  It is
a convenient way to generate declarations and statements by iteration.

    import std.conv: to;

    static foreach(i; 0 .. 10)
    {

        // a 'static foreach' body does not introduce a nested scope
        // (similar to 'static if').

        // The following mixin declaration is at module scope:
        // declares 10 variables x0, x1, ..., x9
        mixin('enum x' ~ to!string(i) ~ ' = i;');
    }

    import std.range: iota;
    // all aggregate types that can be iterated with a standard 'foreach'
    // loop are also supported by static foreach:
    static foreach(i; iota(10))
    {
        // we access the declarations generated in the first 'static foreach'
        pragma(msg, "x", i, ": ", mixin(`x` ~ to!string(i)));
        static assert(mixin(`x` ~ to!string(i)) == i);
    }

    void main()
    {
        import std.conv: text;
        import std.typecons: tuple;
        import std.algorithm: map;
        import std.stdio: writeln;

        // 'static foreach' has both declaration and statement forms
        // (similar to 'static if').

        static foreach(x; iota(3).map!(i => tuple(text("x", i), i)))
        {
            // generates three local variables x0, x1 and x2.
            mixin(text(`int `,x[0],` = x[1];`));

            scope(exit) // this is within the scope of 'main'
            {
                writeln(mixin(x[0]));
            }
        }

        writeln(x0," ",x1," ",x2); // first runtime output
    }

2. Aliases can be created directly from a '__trait'.

Aliases can be created directly from the traits that return symbol(s) or
tuples.  This includes 'getMember', 'allMembers', 'derivedMembers', 'parent',
'getOverloads', 'getVirtualFunctions', 'getVirtualMethods', 'getUnitTests',
'getAttributes' and finally 'getAliasThis'.  Previously an 'AliasSeq' was
necessary in order to alias their return.  Now the grammar allows to write
shorter declarations:

    struct Foo
    {
        static int a;
    }

    alias oldWay = AliasSeq!(__traits(getMember, Foo, "a"))[0];
    alias newWay = __traits(getMember, Foo, "a");

To permit this it was more interesting to include '__trait' in the basic types
rather than just changing the alias syntax. So additionally, wherever a type
appears a '__trait' can be used, for example in a variable declaration:

    struct Foo { static struct Bar {} }
    const(__traits(getMember, Foo, "Bar")) fooBar;
    static assert(is(typeof(fooBar) == const(Foo.Bar)));

3. fix Issue 10100 - Identifiers with double underscores and allMembers

The identifer whitelist has been converted into a blacklist of all possible
internal D language declarations.

Reviewed-on: dlang/dmd#10791
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants