October 04, 2020
On 10/4/20 10:34 PM, kinke wrote:
> But you're solely focusing on static analysis. Static analysis is great but quite obviously limited. Runtime sanitizers are a necessary supplement

It doesn't sit right to design language features that assume sanitizers.
October 04, 2020
On 10/4/20 10:08 PM, Walter Bright wrote:
> On 10/3/2020 7:49 AM, Steven Schveighoffer wrote:
>> `in ref` is a reference, and it's OK if we make this not a reference in practice, because it's const.
> 
> No, it is not. Because a `const ref` can be changed by another mutable reference to the same memory object. This is defined behavior.
> 
> This suggestion turns defined behavior into implementation-defined behavior, meaning it will break existing code written in good faith in unpredictable, unreliable ways.

I will add that C++'s std::min and std::max have had this perennial problem that still bites users. Check https://en.cppreference.com/w/cpp/algorithm/min:

"Warning
Capturing the result of std::min by reference produces a dangling reference if one of the parameters is a temporary and that parameter is returned:

int n = 1;
const int& r = std::min(n-1, n+1);
// r is dangling
"

C++ compilers have gotten increasingly adept at detecting and warning about such situations, but voluntarily adding a problematic feature to the D language to then work on fixing its aftermath doesn't seem a wise thing to do.

October 05, 2020
On Sunday, 4 October 2020 at 20:42:17 UTC, kinke wrote:
> On Sunday, 4 October 2020 at 20:06:49 UTC, Iain Buclaw wrote:
>> If I'd be pressed to bullet point it, I'd put down the following.
>>
>> When an parameter is annotation with `in`:
>> - The parameter is not modifiable.
>> - All memory reachable from the parameter can not be clobbered (overwritten).
>> - The parameter does not escape.
>> - Copy constructors are elided by passing by-ref.
>> - Other forms of copy elision may occur if doing so does not change program behaviour.
>>
>> Is that simple enough for an end-user?
>
> I think that's a pretty good summary, although I'd loosen point 2 to the object itself, not all memory reachable from it.
>

2. Assumes memory reachable from the parameter will not be clobbered (overwritten).

Basically, this:

s.a = 0xacce55ed;
fun(s);
assert(s.a == 0xacce55ed);

The caller can and will optimize based on this assumption never being false.

October 05, 2020
On Sunday, 4 October 2020 at 21:45:03 UTC, Walter Bright wrote:
> On 10/3/2020 7:08 AM, kinke wrote:
>> The idea is that `in` is explicit, and users of a function with `in` params should think of such params as `const T& __restrict__` in C++ terms, which should clarify the potential aliasing problem. Whether the thing is optimized to pass-by-value then shouldn't make any observable difference for the caller.
>
> __restrict__ is a C feature only and never made it into the C++ Standard. I implemented it as a no-op in Digital Mars C. The reason is because it relaxes the rules on what optimizations can be performed in ways that can subtly break code. Very, very few C programmers understand exactly what is going on with it, and sensibly avoid it.
>

I don't think __restrict__ is a good way to reason with expected behaviour.  The spec only makes three things clear: No clobber; No escape; Copy elision if possible.

In is not ref, and is not restrict.  In is in - a value goes in and doesn't come out.
October 05, 2020
On Monday, 5 October 2020 at 02:21:01 UTC, Steven Schveighoffer wrote:
> On 10/4/20 10:19 AM, Iain Buclaw wrote:
>> 3. There is no danger of ref/non-ref mismatches in ABI, because `in` parameters that are inferred `ref` are going to be passed in memory anyway.
>> 
>> In the following:
>> ```
>> alias A = void delegate(in long);
>> alias B = void delegate(const long);
>> ```
>> Either `long` always gets passed in memory, or always gets passed in registers, in both cases, they are always going to be covariant.  The same remains true whatever type you replace `long` with.
>
> As the change says, it's up to the back end to decide but is currently types over 2 machine word size. So this means on 32-bit systems, 80-bit reals would be passed by reference for example.
>
> In practice, I don't know what this means for future or other platform compilers.
>
> But as a trivial counter-example, "the same remains true whatever type you replace `long` with" isn't correct:
>
> struct S
> {
>   size_t[3] data;
> }
>
> alias A = void delegate(in S);
> alias B = void delegate(const S);
>
> static assert(is(A : B)); // fails with -preview=in
>

What did I say about the fault lies with the DMD compiler and not the D language specification?  S can't be both pass in memory and in registers at the same time.

Your example is not a problem with '-preview=in', and shouldn't be construed as one.
October 05, 2020
http://archive.adaic.com/standards/83rat/html/ratl-08-02.html

Very entertaining read as they choose completely different semantics.

October 05, 2020
On 10/4/2020 7:34 PM, kinke wrote:
> But that's not the whole picture. It's about optimizing small-POD cases such as `in float4` - no, I don't want (extremely verbose) `const scope ref float4` to dump the argument to stack, pass a pointer to it in a GP register, and then have the callee load the 4 floats from the address in that GP register into an XMM register again - I want to pass the argument directly in an XMM register. Similar thing for an int - why waste a GP register for an address to something that fits into that register directly?
> With `-preview=in`, this works beautifully for generic templated code too - non-PODs and large PODs are passed by ref, small PODs by value, something C++ can only dream of.

I do understand what it is for.

>> The thing is, I've been working for years on making D as memory safe as we can. This feature is a big step backwards.
> But you're solely focusing on static analysis. Static analysis is great but quite obviously limited. Runtime sanitizers are a necessary supplement, and easy to integrate with LLVM for example - adding support for existing language-independent address and thread sanitizers for LDC was pretty simple, and excluding tests, probably amounted to a few hundred lines, mostly wrt. copying and correctly linking against prebuilt libs.

Static analysis, especially when it is part of the language (not an add-on) is vastly superior to runtime checking. (Runtime checking, such as array bounds overflow checks, can never prove an overflow is not possible. Static analysis can.)

So yes, I very much am focused on static analysis.


> Wrt. aliasing, I think it's more of a general problem, and I guess a `const ref` param being mutated while executing that function is almost always an unintended bug. -preview=in would make that definitely a bug for each `in` param.

See my upcoming talk at #dconf2020 !
October 05, 2020
On 10/4/2020 7:31 PM, Steven Schveighoffer wrote:
> A *caller* can know that two parameters are aliased (trivially -- pass the same parameter twice), and in that case, expect a certain outcome.

Unfortunately, only in the trivial case can the caller detect it.
October 05, 2020
On 10/4/2020 7:19 AM, Iain Buclaw wrote:
> Plucking a fitting example from a Phobos unittest that demonstrates the kind of things DMD let's people get away with:
> ```
> RefCounted!int* p;
> {
>      auto rc1 = RefCounted!int(5);
>      p = &rc1;
>      assert(rc1 == 5);
>      assert(rc1._refCounted._store._count == 1);
>      auto rc2 = rc1;
>      assert(rc1._refCounted._store._count == 2);
> }
> assert(p._refCounted._store == null);
> ```
> Why is this not a compile-time error?  DMD just isn't punishing users enough for the buggy code they've written.

It's not allowed in @safe code.
October 05, 2020
On 10/4/2020 7:19 AM, Iain Buclaw wrote:
> I've also skimmed past a passing concern that the ABI between compilers would be different.  Well, let me rest assure you that DMD, GDC and LDC have never been compatible in the first place, so there's no point worrying about that now.

The problem is not dmd compiled code calling gdc/ldc compiled code. The problem is code that works with one compiler fails with another, because the "to ref or not to ref" decision is *implementation defined*.