Jump to page: 1 2
Thread overview
D Language Foundation July 2022 Quarterly Meeting Summary
Aug 28, 2022
Mike Parker
Sep 02, 2022
Bastiaan Veelo
Aug 28, 2022
jmh530
Aug 28, 2022
Sergey
Aug 29, 2022
zjh
Aug 29, 2022
zjh
Aug 29, 2022
Ruby The Roobster
Aug 29, 2022
zjh
Aug 29, 2022
zjh
Aug 29, 2022
ryuukk_
Aug 29, 2022
12345swordy
Aug 29, 2022
Dukc
Sep 01, 2022
Iain Buclaw
Sep 02, 2022
Bastiaan Veelo
August 28, 2022

This summary is quite a bit overdue. Sorry for the delay.

The July 8 D Language Foundation meeting was one of our quarterly meetings. In the first part of these meetings, representatives from industry join us to provide us with updates, notify us of issues they're experiencing, and provide us with feedback and ideas. The second part is the typical monthly meeting addressing foundation business. The industry reps are always invited to stick around for it and usually do.

For this meeting, we had two new faces join us: Paul and Robert Toth of Ucora. They let us know back in June that the company has been using D for years. We invited them to join us for our quarterlies, and they accepted. We hope we can help resolve any D issues they may face, and we welcome their feedback on the D user experience.

The meeting

The meeting took place on July 8 at 14:00 UTC. The following people attended:

Walter Bright (DLF)
Iain Buclaw (DLF/GDC)
Ali Çehreli (DLF/Mercedes Benz R & D North America)
Max Haughton (DLF/Symmetry)
Martin Kinkelin (DLF/LDC)
Dennis Korpel (DLF)
Mario Kröplin (Funkwerk)
Mathias Lang (DLF/Symmetry)
Razvan Nitu (DLF)
Robert Schadek (Symmetry)
Paul Toth (Ucora)
Robert Toth (Ucora)
Bastiaan Veelo (SARC)
Joseph Rushton Wakeling (Frequenz)

Mario

Funkwerk had a problem with Issue #23234. There was a little discussion about the issue, and that Funkwerk has a workaround for now, but Razvan put it on his list of priority issues and subsequently resolved it.

Mario said they sometimes run into issues like this when they update the compiler after a long time, or when Mathis when they try something weird (i.e., when Mathis has ideas and they find new areas in the compiler that don't work as expected). On the other hand, they've been using D for well over a decade. They have their own set of libraries and the solutions to their problems are usually straightforward. D has been a big help in their work.

Bastiaan

SARC has marked a major milestone in that their 500KLOC Extended Pascal codebase has been completely transcompiled to D (if you aren't aware of this project, you might enjoy Bastiaan's talks from DConf 2017, Extending Pegged to Parse Another Programming Language, and DConf 2019, Transcompilation into D). Now they are increasing their testing and working out other steps they need to take before going into production.

One of those steps is internationalization. For that, they're using GNU getext, and a couple of weeks before the meeting had released their D package for interfacing it on dub. He said they would soon be releasing a package for string extraction, and that with that we'd have comprehensive support for gettext in D. He said everything is looking good, though they do have a couple of nasty bugs for which he hopes to find a solution.

Robert S.

Here I can quote Robert in full:

>

Not much to report. The compiler's too slow. Other than that, D is the best language.

Joe

And I can quote Joe in full following Robert:

>

What he said I think. Nothing to add from my side.

Paul & Robert T.

Robert noted that one of their new programmers has had a hard time getting up to speed with D via the existing documentation.

Ucora have been working with D for about 7 years, and their primary product is written in D, so they are heavily invested. Paul's interest is in the accessibility of D to the wider programming community. He was pleased with the response to the post he made in June with a suggestion for the D documentation. Being new to the community, for now he's just observing, but they are interested in helping out in any way they can.

Bastiaan noted that in Paul's forum thread he requested that Ucora be added to the Orgs Using D page. Max had opened a PR, but then got busy with other things (and has since been unable to build the website). The PR hasn't yet been merged, but I'll see about making that happen as soon as I publish this summary.

Paul said that for his own purposes, the documentation is well-written and he can find what he needs. But there is an initial hurdle to get over to be able to use the documentation effectively. New programmers rely on a programming language's manual when learning the language, so he's interested in ways of making D's documentation more accessible for new users. Enabling comments on documentation pages was the only concrete suggestion he had now. Walter suggested this could be done by creating a forum thread for each page in the documentation where people can post comments.

I noted that we have previously discussed overhauling the website and that a complete evaluation of the UX should be part of that. This is something that will fall under the purview of the ecosystem management team when it's in place.

We then discussed the topic of the user experience of documentation which touched on the experience with programmers at Ucora (in using the documentation and why they aren't using some of the more powerful features of D), comments on PHP doc pages, the D Tour, different kinds of documentation (reference vs. tutorial vs. how-to guides), Ali's book, ways to delineate them, and more.

Some good ideas were brought forward. Overhauling dlang.org and the D UX as a whole is something we want to get done as soon as we can after we get our ecosystem services sorted.

Mathias

Mathias had a few things to bring up.

The first was Issue 23164, which he encountered when preparing his DConf talk. In short, it looks like dmd is moving structs that have copy constructors. This has resulted in portions of our core.stdcpp being disabled on different platforms. But LDC and GDC work as expected. Walter said the first problem is that the example has an internal pointer, which is noted in the D spec as something that shouldn't be done. He said the second thing is he doesn't think dmd is moving structs, even though the spec says it can. He thinks what's happening is that one of the copy constructors is passing an rvalue, causing a copy onto the stack and not updating the internal pointers. But Mathias said it's working as expected with LDC and GDC, and Andrei had said in the past prohibiting internal struct pointers is untenable. (This has all since been noted in the Bugzilla issue).

Ultimately, Walter suggested the example be further reduced to determine exactly where the problem is. There's too much going on with it right now. Mathias said he would try to do that.

Iain noted that GDC's behavior can be attributed to an internal flag attached to non-trivial structs that prevent it from being copied around, the compiler will crash instead. That means this kind of struct is always passed and returned by reference.

The second thing Mathias brought up is that he had been working on improvements to dub to support more use cases and output better error messages. He encountered a blocker when he had to enable DIP 1000 on some code and ended up with memory corruption. He hadn't yet submitted an issue for it at the time (and I don't know if he has submitted one since).

The third thing was the merger of the DMD and DRuntime repositories into a monorepo, which was something he and others had scheduled for the next day. Mathias warned that there might be some disruptions in CI and such as a result. He asked if anyone had any objections to the move. No one did. The merger was subsequently carried out. It did cause a few hiccups, but Mathias, Iain, and whomever else was involved handled them all (as far as I know).

Next, he said that we have a problem with some preview/revert switches (and it arose specifically with -preview=in). DRuntime and Phobos need to be compiled with a certain set of flags. If the flags used when compiling user code don't match those flags, we have a problem. We want a period where the user may or may not be using a certain preview, but if that preview changes the ABI, it doesn't work. One idea he had for this is a new pragma that could be put at the top of a module to specify which flags it needs to be compiled with, but it's not very elegant. He was hoping multiple brains could come up with something better. This led to some discussion about whether the pragma would work (Walter doesn't think so).

Walter said the only way he sees around this is that preview features shouldn't change the ABI, but one thing that could help is moving to header-only libraries. Martin suggested this happens with DRuntime and Phobos because they're the only pre-compiled libraries we have in the ecosystem. If they were dub projects, the problem goes away. In the end, no solution presented itself in this meeting, so we deferred this to a future monthly meeting.

Next, Mathias brought up the two ways to define C++ namespaces in D, one using an identifier and the other using a string. The former came first, with the latter being added later to address deficiencies in that approach. Mathias had encountered problems in his work with C++ interop using the identifier version and has since ignored it completely, relying on the string version. Plus, having two alternatives is confusing to people with no experience in D and C++ interop. He would like to deprecate the identifier option. Walter recalls past discussions about this, but can't remember the details about the tradeoffs. So he had no answer at the time and suggested they talk about this at DConf (I don't know if they did).

Finally, he talked about how core.stdcpp is compiled into DRuntime and is dependent on a specific C++ runtime. Initially, it was added to DRuntime on the premise that it's equivalent to the C bindings already there. The problem is that the C bindings don't cause any symbols to be generated, but the C++ bindings do. This can result in an ABI mismatch. Mathias thinks they should be out of the runtime. Walter agreed.

Martin

Martin still has some annoying issues with semantic ordering and cycles. Apart from that, he had been waiting for the 2.100.1 release of DMD, and if it didn't happen in the next week or so he would go ahead and push out a new release of LDC anyway (which he did on July 20).

He said the move to a monorepo for DMD and DRuntime provided the opportunity to undo some mistakes made with LDC's repo in the past by moving it also to a monorepo. It also presented some challenges, but most of them had been cleared up. He expected everything to be fine. It would simplify things for LDC in the future and he was looking forward to the merger.

Iain

Iain has done a lot of internal GDC work which mostly benefits him and anyone using GDC as their main compiler. He had been doing a bit of tech support for Bruce Carneal related to SIMD. That has resulted in some new SIMD intrinsics. He had also moved about 600 lines of code out of the compiler and into the library.

Upstream, he had added float and double precision implementations of the log family functions in std.math. This has the potential to break compilation for projects passing integrals to these functions. Additionally, logb is now pure.

On infrastructure, he had seen no pushback on the monorepo from anyone heavily involved with the DMD and DRruntime repos. The only thing left to test was whether the pipelines would be okay after the change. There were no issues running the test suites locally.

On DMD releases, Martin Nowak has less and less time to do them. Iain was in the process of taking over. The intent is to take move the specifics away from one person (e.g., having secret keys on one person's laptop) and set things up on GitHub runners so that anyone on the DMD core team can trigger a release. He still had blockers from making that happen. The first was that he did not have access to the downloads.dlang.org server. The two people with the AWS credentials had been unresponsive (though I believe that has since changed). The second is that he has no certificates with which to do code signing for the Windows binaries.

Since the meeting, Iain and I have set up a new server for the archived downloads. We've moved away from AWS and are now using Backblaze (with free bandwidth, thanks to the Bandwith Alliance and our use of Cloudflare). All 235.2 GB of DMD downloads have been moved. Iain can correct me if I'm wrong, but I believe this is active right now, so anything you download from downloads.dlang.org will come from there. As for the code-signing certificate, we're trying to decide on an option that's best for us. In the meantime, the last I heard, Martin Nowak was going to put out a release of 2.100.1 without signing the Windows executable.

Me

I told everyone I'd not had an update from Vladimir Panteleev or Petar Kirov regarding our migration of ecosystem services to foundation control. I'd created accounts with Hertzner, Netlify, and Cloudflare, and granted Petar admin access to them. Petar had since become busy, and I anticipated we'd have to wait until after DConf for any progress. That turned out to be the case.

We've since migrated our DNS to Cloudflare (which some of you may have noticed a couple of weeks ago when we had outages for some subdomains) and I've granted Cloudflare access to more administrators (Iain, Vladimir, and Mathias). We hadn't originally intended for the downloads to be the first service migrated---that was dictated by circumstances---but now that it's done we can look at migrating the next thing. I'm not sure at the moment what that will be, but I'll announce it here when I know.

Razvan

Razvan had nothing to report.

Dennis

Dennis brought up a discussion about reducing the size of object.d that came up in a PR thread. Specifically, moving things into separate modules, then making object.d a list of public imports would make it easier to maintain a custom object.d.

Walter said that the bigger object.d gets, the slower it compiles, but splitting it into multiple modules would make it slower. Compilation speed is less dependent on file size and more dependent on the number of files. We may be expecting too much from object.d, but he had no answer for now. We need to be more parsimonious about adding things to it.

Martin had no magic answer either but said he's always seen object.d as the foundation of DRuntime. Any pay-as-you-go runtime would need to start from there. But he noted that most of the recent size increase is because of the project converting DRuntime hooks to templates. He suggested those imports could be removed from object.d, and the compiler can insert imports explicitly where they are used so that they aren't part of the compilation for modules that don't use those hooks.

Next, Dennis talked about the focus of the PR that brought about the discussion: that symbols like destroy are part of the global namespace (the PR added the ability to disable destroy via a special version identifier). Should we be concerned about this? The consensus was that with D's built-in ability to differentiate symbols (FQN, static imports), this isn't a problem. Iain closed the PR after the meeting, noting that we agreed something needs to be done about object.d, "but not this".

The third thing Dennis brought up was a recent thread about private-to-the-module, and how some people would prefer that it be private-to-the-class instead. He doesn't think the feature should change, but he did put together an implementation to try it out. He asked if this was going to be dead on arrival. The consensus was "yes", with Martin noting that what he liked most was Dennis's "thumbs down" for his own pull request. We did have a short discussion about this, with Martin noting that this isn't a problem if you're properly encapsulating your types. It's only if you're used to older codebases or other languages that it may seem to be a problem, but in D we don't need to worry about it. Dennis subsequently closed the PR.

As part of that discussion, I raised the issue that came up in the aforementioned forum thread: we treat the private API of the module as part of the class/struct's private API, but we don't do the same for the public API. Meaning, calls through the public API of a class always trigger its invariant, but calls through a module's public API do not. This means it's safe for the class to modify its internal state through its private API, but not for the module to do so (similarly, a module's public API does not obtain the lock of a synchronized class). I think it's something most programmers don't consider, and we should think of some way to address it. Though, we didn't do that in this meeting :-)

Max

Max said he had several PRs he needed to finish. For one of them, he had implemented with expressions to play with in a branch, something he and John Colvin are fond of. He had also opened a newCTFE branch from Stefan Koch's old work. He and Nicholas Wilson had been going through to get rid of problematic code. His opinion was that we should get rid of its warts, refactor it, and upstream it. The preview flag was enabled so people could play with it, but it will revert to the old engine in cases where it fails.

Martin wanted to know more about the revival, as he was under the impression that Stefan had given up on it because it didn't provide the performance gains he had expected. Max estimated it should generally be between 5 and 10 times faster, but that it can be much faster in situations where the old engine gets bogged down. In contrast, the JIT in SDC (which uses the LLVM JIT API) can be up to 50 times faster. But for Max, performance isn't as important for newCTFE considering that the code is easier to work with and it uses less memory.

Martin then brought up the fact that newCTFE had been restricted to using 64-bit doubles and asked if that's still the case. Max affirmed that's so, and Martin said that won't do. Max suggested that since we now have ImportC, we could just use it to compile x87 soft floats, assuming the license is compatible. Martin said we should focus on x87 too much. It's interesting for DMD as it's x86 only. GCC/GDC uses soft float emulation across platforms, and LDC uses the host platform's real. We have to decide if we want an abstract precision across all platforms like GDC is using, or try to emulate the target precision on any host. What's important is that newCTFE must be able to cope with arbitrary real_t types as the frontend currently can. Max thinks it's doable with some refactoring.

Walter

Walter said he had lost a lot of time in the preceding few weeks trying to debug failures in the test suite that can't be reproduced. He had been complaining about the lack of information in the test suite logs. He cited a specific example where running the test online showed a failure in a file, but gave no other error messages about that file, but then running the test suite locally resulted in a useful error message in the log. He suspected it might be a stdout vs stderr issue. It's a general problem he has. Further, the tests put out log files that are thousands of lines long, making it difficult to pinpoint the actual problem. Sometimes, an error just disappears on a subsequent run and he doesn't always understand why. He had noticed that it sometimes happens after he rebases a PR. He noted that the autotester rebases automatically, so the other tests should, too.

Iain noted that the need to rebase should disappear once DMD and DRuntime were merged, as those two repos being out of sync was usually the culprit.

Another issue driving him nuts was running cpp on Mac for ImportC. The problem is that cpp on the autotester fails on some of the Mac system header files. So he was wondering if anyone knew which C preprocessor he should be invoking on Mac. Mathias noted that the autotester's Mac version is probably ancient, but suggested he invoke clang with the switch to use the C preprocessor rather than invoking the preprocessor directly. Walter said that was a good idea.

Next, he said he had made a lot of progress on getting ImportC to compile the C files in DRuntime. It was working Phobos except for the Posix files. Every Posix platform was failing differently, but he hadn't figured it out yet.

He said that other than that, things seem to be going rather well.

The next meeting

Given the lateness of this summary, the next meeting has already happened. It was a monthly meeting that took place on August 4 during the DConf Hackathon. I'll publish a summary of that in the next couple of days.

The next, next meeting is happening Friday, September 2, at 14:00 UTC. If you'd like to bring something to us, please let me know and I'll see about putting you on the agenda.

August 28, 2022

On 8/28/22 6:37 AM, Mike Parker wrote:

>

SARC has marked a major milestone in that their 500KLOC Extended Pascal codebase has been completely transcompiled to D

This is awesome! I remember that talk, and it was very interesting.

Congratulations!

-Steve

August 28, 2022

On Sunday, 28 August 2022 at 10:37:03 UTC, Mike Parker wrote:

>

This summary is quite a bit overdue. Sorry for the delay.
[...]

Thanks for the detailed update.

August 28, 2022

On Sunday, 28 August 2022 at 10:37:03 UTC, Mike Parker wrote:

>

This summary is quite a bit overdue. Sorry for the delay.

One of the most interesting posts! Thank you for sharing that, Mike. And for the work of all participants and committers.

August 29, 2022

On Sunday, 28 August 2022 at 10:37:03 UTC, Mike Parker wrote:

>

Compilation speed is less dependent on file size and more dependent on the number of files.

Isn't that strange?

August 29, 2022

On Monday, 29 August 2022 at 02:58:33 UTC, zjh wrote:

>

On Sunday, 28 August 2022 at 10:37:03 UTC, Mike Parker wrote:
and how some people would prefer that it be private-to-the-class instead.

Encapsulation by class can quickly determine the problem.
The smaller the encapsulate, the better.
It would be better if struct could also be inherited.

August 29, 2022

On Monday, 29 August 2022 at 02:58:33 UTC, zjh wrote:

>

On Sunday, 28 August 2022 at 10:37:03 UTC, Mike Parker wrote:

>

Compilation speed is less dependent on file size and more dependent on the number of files.

Isn't that strange?

If so, split the files for editing and merge them for compiling.

August 29, 2022

On Monday, 29 August 2022 at 03:04:15 UTC, zjh wrote:

>

On Monday, 29 August 2022 at 02:58:33 UTC, zjh wrote:

>

On Sunday, 28 August 2022 at 10:37:03 UTC, Mike Parker wrote:
and how some people would prefer that it be private-to-the-class instead.

Encapsulation by class can quickly determine the problem.
The smaller the encapsulate, the better.
It would be better if struct could also be inherited.

The whole point of a struct in an OOP Language is to allow user defined types that prohibit inheritance.

August 29, 2022

On Monday, 29 August 2022 at 03:32:08 UTC, Ruby The Roobster wrote:

>

The whole point of a struct in an OOP Language is to allow user defined types that prohibit inheritance.

Why do someone like C++, because it gives you freedom, no matter what you do.

August 29, 2022
>

Dennis brought up a discussion about reducing the size of object.d that came up in a PR thread. Specifically, moving things into separate modules, then making object.d a list of public imports would make it easier to maintain a custom object.d.

It is unfortunate that my suggestion was used to push an other idea

A public import, is the same problem

My issue with object.d is it is shadowing symbols

destroy for example

If i want a destroy function, i call destroy, but if i made a typo in my function or forgot to actually create it, then it'll silently call destroy from object.d without letting you know about it, that's what prompted me to make the PR

I now banned the destroy name, and instead use dispose as people on the IRC suggested me to do, wich in restrospec is a better word indeed

But i figured i should not expect such changes, so instead i now compile with -betterC and a custom object.d for all of my projects

The other unfortunate thing is i now have to deal with issues like this, and incorporate hacks into my codebases for LDC: https://github.com/ldc-developers/ldc/issues/2425#issuecomment-1193330845

>

The consensus was that with D's built-in ability to differentiate symbols (FQN, static imports), this isn't a problem. Iain closed the PR after the meeting, noting that we agreed something needs to be done about object.d, "but not this".

The worst part is that file should not be needed at all to begin with

You can't do simple ptr/slice casting without it, and you can't use enums without it

If destroy is essential to D, then it should be upgraded to a builtin and reserved keyword, if it is just an utility function, then it shouldn't be in the global scope, or it should be prefixed __destroy

There needs a way to differentiate functions used for language support like switch and cast with other utility functions, 2 different concerns

This is what i have to carry to opt out this specific thing (luckily i do not use any other features): (see at the end of the post)

And one recent issue i had, because of the custom object.d route:

https://i.imgur.com/Ye9ewJP.png

I couldn't figure out what caused the issue, thanks to Adam, compiling with dmd -v pointed out the problematic import: import core.sys.windows.threadaux;

Pay as you go is the way to go, but the experience should be drastically improved imo

module object;

alias size_t = typeof(int.sizeof);
alias ptrdiff_t = typeof(cast(void*)0 - cast(void*)0);

alias sizediff_t = ptrdiff_t; // For backwards compatibility only.
/**
 * Bottom type.
 * See $(DDSUBLINK spec/type, noreturn).
 */
alias noreturn = typeof(*null);

alias hash_t = size_t; // For backwards compatibility only.
alias equals_t = bool; // For backwards compatibility only.

alias string  = immutable(char)[];
alias wstring = immutable(wchar)[];
alias dstring = immutable(dchar)[];


bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs)
@nogc nothrow pure @trusted
if (__traits(isScalar, T1) && __traits(isScalar, T2))
{
    if (lhs.length != rhs.length)
        return false;

    static if (T1.sizeof == T2.sizeof
        // Signedness needs to match for types that promote to int.
        // (Actually it would be okay to memcmp bool[] and byte[] but that is
        // probably too uncommon to be worth checking for.)
        && (T1.sizeof >= 4 || __traits(isUnsigned, T1) == __traits(isUnsigned, T2))
        && !__traits(isFloating, T1) && !__traits(isFloating, T2))
    {
        if (!__ctfe)
        {
            // This would improperly allow equality of integers and pointers
            // but the CTFE branch will stop this function from compiling then.

            import core.stdc.string : memcmp;
            return lhs.length == 0 ||
                0 == memcmp(cast(const void*) lhs.ptr, cast(const void*) rhs.ptr, lhs.length * T1.sizeof);
        }
    }


    foreach (const i; 0 .. lhs.length)
    {
        static if (__traits(isStaticArray, at(lhs, 0))) // "Fix" for -betterC
        {
            if (at(lhs, i)[] != at(rhs, i)[]) // T1[N] != T2[N] doesn't compile with -betterC.
                return false;
        }
        else
        {
            if (at(lhs, i) != at(rhs, i))
                return false;
        }
    }
    return true;
}

bool __equals(T1, T2)(scope T1[] lhs, scope T2[] rhs)
if (!__traits(isScalar, T1) || !__traits(isScalar, T2))
{
    if (lhs.length != rhs.length)
    {
        return false;
    }
    if (lhs.length == 0)
        return true;

    static if (useMemcmp!(T1, T2))
    {
        if (!__ctfe)
        {
            static bool trustedMemcmp(scope T1[] lhs, scope T2[] rhs) @trusted @nogc nothrow pure
            {
                pragma(inline, true);
                import core.stdc.string : memcmp;
                return memcmp(cast(void*) lhs.ptr, cast(void*) rhs.ptr, lhs.length * T1.sizeof) == 0;
            }
            return trustedMemcmp(lhs, rhs);
        }
        else
        {

            foreach (const i; 0 .. lhs.length)
            {
                static if (__traits(isStaticArray, at(lhs, 0))) // "Fix" for -betterC
                {
                    if (at(lhs, i)[] != at(rhs, i)[]) // T1[N] != T2[N] doesn't compile with -betterC.
                        return false;
                }
                else
                {
                    if (at(lhs, i) != at(rhs, i))
                        return false;
                }
            }
            return true;
        }
    }
    else
    {

    foreach (const i; 0 .. lhs.length)
    {
        static if (__traits(isStaticArray, at(lhs, 0))) // "Fix" for -betterC
        {
            if (at(lhs, i)[] != at(rhs, i)[]) // T1[N] != T2[N] doesn't compile with -betterC.
                return false;
        }
        else
        {
            if (at(lhs, i) != at(rhs, i))
                return false;
        }
    }
        return true;
    }
}

pragma(inline, true)
ref at(T)(T[] r, size_t i) @trusted
    // exclude opaque structs due to https://issues.dlang.org/show_bug.cgi?id=20959
    if (!(is(T == struct) && !is(typeof(T.sizeof))))
{
    static if (is(immutable T == immutable void))
        return (cast(ubyte*) r.ptr)[i];
    else
        return r.ptr[i];
}

template BaseType(T)
{
    static if (__traits(isStaticArray, T))
        alias BaseType = BaseType!(typeof(T.init[0]));
    else static if (is(immutable T == immutable void))
        alias BaseType = ubyte;
    else static if (is(T == E*, E))
        alias BaseType = size_t;
    else
        alias BaseType = T;
}

template useMemcmp(T1, T2)
{
    static if (T1.sizeof != T2.sizeof)
        enum useMemcmp = false;
    else
    {
        alias B1 = BaseType!T1;
        alias B2 = BaseType!T2;
        enum useMemcmp = __traits(isIntegral, B1) && __traits(isIntegral, B2)
           && !( (B1.sizeof < 4 || B2.sizeof < 4) && __traits(isUnsigned, B1) != __traits(isUnsigned, B2) );
    }
}



TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from)
{
   const fromSize = from.length * TFrom.sizeof;
   const toLength = fromSize / TTo.sizeof;

   if ((fromSize % TTo.sizeof) != 0)
   {
        //onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof);
        assert(0);
   }

   struct Array
   {
       size_t length;
       void* ptr;
   }
   auto a = cast(Array*)&from;
   a.length = toLength; // jam new length
   return *cast(TTo[]*)a;
}



// switch
extern(C) void __switch_error()(string file = __FILE__, size_t line = __LINE__)
{
    //__switch_errorT(file, line);
    wasm.abort();
}

extern(C) int __switch(T, caseLabels...)(/*in*/ const scope T[] condition) pure nothrow @safe @nogc
{
    // This closes recursion for other cases.
    static if (caseLabels.length == 0)
    {
        return int.min;
    }
    else static if (caseLabels.length == 1)
    {
        return __cmp(condition, caseLabels[0]) == 0 ? 0 : int.min;
    }
    // To be adjusted after measurements
    // Compile-time inlined binary search.
    else static if (caseLabels.length < 7)
    {
        int r = void;
        enum mid = cast(int)caseLabels.length / 2;
        if (condition.length == caseLabels[mid].length)
        {
            r = __cmp(condition, caseLabels[mid]);
            if (r == 0) return mid;
        }
        else
        {
            // Equivalent to (but faster than) condition.length > caseLabels[$ / 2].length ? 1 : -1
            r = ((condition.length > caseLabels[mid].length) << 1) - 1;
        }

        if (r < 0)
        {
            // Search the left side
            return __switch!(T, caseLabels[0 .. mid])(condition);
        }
        else
        {
            // Search the right side
            return __switch!(T, caseLabels[mid + 1 .. $])(condition) + mid + 1;
        }
    }
    else
    {
        // Need immutable array to be accessible in pure code, but case labels are
        // currently coerced to the switch condition type (e.g. const(char)[]).
        pure @trusted nothrow @nogc asImmutable(scope const(T[])[] items)
        {
            assert(__ctfe); // only @safe for CTFE
            immutable T[][caseLabels.length] result = cast(immutable)(items[]);
            return result;
        }
        static immutable T[][caseLabels.length] cases = asImmutable([caseLabels]);

        // Run-time binary search in a static array of labels.
        return __switchSearch!T(cases[], condition);
    }
}

extern(C) int __cmp(T)(scope const T[] lhs, scope const T[] rhs) @trusted
    if (__traits(isScalar, T))
{
    // Compute U as the implementation type for T
    static if (is(T == ubyte) || is(T == void) || is(T == bool))
        alias U = char;
    else static if (is(T == wchar))
        alias U = ushort;
    else static if (is(T == dchar))
        alias U = uint;
    else static if (is(T == ifloat))
        alias U = float;
    else static if (is(T == idouble))
        alias U = double;
    else static if (is(T == ireal))
        alias U = real;
    else
        alias U = T;

    static if (is(U == char))
    {
        import core.internal.string : dstrcmp;
        return dstrcmp(cast(char[]) lhs, cast(char[]) rhs);
    }
    else static if (!is(U == T))
    {
        // Reuse another implementation
        return __cmp(cast(U[]) lhs, cast(U[]) rhs);
    }
    else
    {
        version (BigEndian)
        static if (__traits(isUnsigned, T) ? !is(T == __vector) : is(T : P*, P))
        {
            if (!__ctfe)
            {
                int c = mem.memcmp(lhs.ptr, rhs.ptr, (lhs.length <= rhs.length ? lhs.length : rhs.length) * T.sizeof);
                if (c)
                    return c;
                static if (size_t.sizeof <= uint.sizeof && T.sizeof >= 2)
                    return cast(int) lhs.length - cast(int) rhs.length;
                else
                    return int(lhs.length > rhs.length) - int(lhs.length < rhs.length);
            }
        }

        immutable len = lhs.length <= rhs.length ? lhs.length : rhs.length;
        foreach (const u; 0 .. len)
        {
            static if (__traits(isFloating, T))
            {
                immutable a = lhs.ptr[u], b = rhs.ptr[u];
                static if (is(T == cfloat) || is(T == cdouble)
                    || is(T == creal))
                {
                    // Use rt.cmath2._Ccmp instead ?
                    auto r = (a.re > b.re) - (a.re < b.re);
                    if (!r) r = (a.im > b.im) - (a.im < b.im);
                }
                else
                {
                    const r = (a > b) - (a < b);
                }
                if (r) return r;
            }
            else if (lhs.ptr[u] != rhs.ptr[u])
                return lhs.ptr[u] < rhs.ptr[u] ? -1 : 1;
        }
        return (lhs.length > rhs.length) - (lhs.length < rhs.length);
    }
}

// This function is called by the compiler when dealing with array
// comparisons in the semantic analysis phase of CmpExp. The ordering
// comparison is lowered to a call to this template.
extern(C) int __cmp(T1, T2)(T1[] s1, T2[] s2)
if (!__traits(isScalar, T1) && !__traits(isScalar, T2))
{
    alias U1 = Unqual!T1;
    alias U2 = Unqual!T2;

    static if (is(U1 == void) && is(U2 == void))
        static @trusted ref inout(ubyte) at(inout(void)[] r, size_t i) { return (cast(inout(ubyte)*) r.ptr)[i]; }
    else
        static @trusted ref R at(R)(R[] r, size_t i) { return r.ptr[i]; }

    // All unsigned byte-wide types = > dstrcmp
    immutable len = s1.length <= s2.length ? s1.length : s2.length;

    foreach (const u; 0 .. len)
    {
        static if (__traits(compiles, __cmp(at(s1, u), at(s2, u))))
        {
            auto c = __cmp(at(s1, u), at(s2, u));
            if (c != 0)
                return c;
        }
        else static if (__traits(compiles, at(s1, u).opCmp(at(s2, u))))
        {
            auto c = at(s1, u).opCmp(at(s2, u));
            if (c != 0)
                return c;
        }
        else static if (__traits(compiles, at(s1, u) < at(s2, u)))
        {
            if (at(s1, u) != at(s2, u))
                return at(s1, u) < at(s2, u) ? -1 : 1;
        }
        else
        {
            // TODO: fix this legacy bad behavior, see
            // https://issues.dlang.org/show_bug.cgi?id=17244
            static assert(is(U1 == U2), "Internal error.");
            auto c = (() @trusted => mem.memcmp(&at(s1, u), &at(s2, u), U1.sizeof))();
            if (c != 0)
                return c;
        }
    }
    return (s1.length > s2.length) - (s1.length < s2.length);
}


template Unqual(T : const U, U)
{
    static if (is(U == shared V, V))
        alias Unqual = V;
    else
        alias Unqual = U;
}


// binary search in sorted string cases, also see `__switch`.
private int __switchSearch(T)(/*in*/ const scope T[][] cases, /*in*/ const scope T[] condition) pure nothrow @safe @nogc
{
    size_t low = 0;
    size_t high = cases.length;

    do
    {
        auto mid = (low + high) / 2;
        int r = void;
        if (condition.length == cases[mid].length)
        {
            r = __cmp(condition, cases[mid]);
            if (r == 0) return cast(int) mid;
        }
        else
        {
            // Generates better code than "expr ? 1 : -1" on dmd and gdc, same with ldc
            r = ((condition.length > cases[mid].length) << 1) - 1;
        }

        if (r > 0) low = mid + 1;
        else high = mid;
    }
    while (low < high);

    // Not found
    return -1;
}
« First   ‹ Prev
1 2