Skip to content

Fix Issue 19754 - Re-apply #9505 and fix it, making isLvalue() logic more restrictive wrt. literals#10124

Merged
dlang-bot merged 9 commits intodlang:masterfrom
kinke:fix19754
Sep 25, 2020
Merged

Fix Issue 19754 - Re-apply #9505 and fix it, making isLvalue() logic more restrictive wrt. literals#10124
dlang-bot merged 9 commits intodlang:masterfrom
kinke:fix19754

Conversation

@kinke
Copy link
Contributor

@kinke kinke commented Jul 2, 2019

These issues have surfaced with #9505 (mainly with non-DMD backends).

@dlang-bot
Copy link
Contributor

dlang-bot commented Jul 2, 2019

Thanks for your pull request and interest in making D better, @kinke! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.
Please verify that your PR follows this checklist:

  • My PR is fully covered with tests (you can see the coverage diff by visiting the details link of the codecov check)
  • My PR is as minimal as possible (smaller, focused PRs are easier to review than big ones)
  • I have provided a detailed rationale explaining my changes
  • New or modified functions have Ddoc comments (with Params: and Returns:)

Please see CONTRIBUTING.md for more information.


If you have addressed all reviews or aren't sure how to proceed, don't hesitate to ping us with a simple comment.

Bugzilla references

Auto-close Bugzilla Severity Description
19754 normal cast() sometimes yields lvalue, sometimes yields rvalue
20608 regression [REG2.087] Cannot pass tuple.expand to auto ref T... template argument pack

⚠️⚠️⚠️ Warnings ⚠️⚠️⚠️

To target stable perform these two steps:

  1. Rebase your branch to upstream/stable:
git rebase --onto upstream/stable upstream/master
  1. Change the base branch of your PR to stable

Testing this PR locally

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

dub run digger -- build "master + dmd#10124"

}

bool binOptimize(BinExp e, int flags)
bool binOptimize(BinExp e, int flags, bool keepLhsLvalue = false)
Copy link

Choose a reason for hiding this comment

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

No default parameters (rule 12)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are 14 other call sites in this file I'd rather not touch.

Copy link

Choose a reason for hiding this comment

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

Let's say it's okay since it's not a new func.

@kinke
Copy link
Contributor Author

kinke commented Jul 2, 2019

https://github.com/dlang/dmd/blob/master/test/fail_compilation/test6883.d doesn't fail anymore (compile-time bounds checks).

@kinke
Copy link
Contributor Author

kinke commented Jul 4, 2019

In the 3rd commit, I tried to tentatively optimize the IndexExp even for keepLvalue = true and to 'undo' it in case it yields no lvalue (no idea if it ever would) - because that's where the compile-time errors for trivial out-of-bounds cases are generated (as part of calling Index() from constfold.d). The expected errors are back, but now twice, as each assignment is optimize()d twice - once as part of AssignExp-sema (where the 1st error is generated, but now not left at the optimized constant lhs, but 'undone' to the partially optimized IndexExp), and then again as ExpStatement-sema. Apparent hacks/architectural flaws (need to try to const-fold (=> rvalue) an assignment's lhs for compile-time out-of-bounds errors in that lhs) seem to be surfacing...

@kinke
Copy link
Contributor Author

kinke commented Jul 5, 2019

Just stumbling from one problem to the next, I'll probably give up on this. I'd suggest reverting #9505 for the time being, it has opened a can of worms and doesn't work as intended. Pinging @ibuclaw.

@kinke kinke force-pushed the fix19754 branch 6 times, most recently from 1faa809 to 5b6e828 Compare July 6, 2019 02:08
@kinke
Copy link
Contributor Author

kinke commented Jul 6, 2019

After much trial and error, there seems to be light at the end of the tunnel.

The Phobos unittest compile errors are due to some refRange(&["foobar"][0])... instead of string s = "foobar"; refRange(&s)..., as ["foobar"][0] is now an rvalue (as is now enum s = ["foobar"], s[0]). To me, it seems like a questionable shortcut to save a line; the original testcase in the associated issue report uses the explicit 2nd version.

@kinke kinke force-pushed the fix19754 branch 6 times, most recently from e7a3337 to 2423d88 Compare July 6, 2019 04:24
override bool isLvalue()
{
return true;
return e1.op != TOK.structLiteral;
Copy link

@ghost ghost Jul 7, 2019

Choose a reason for hiding this comment

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

The problem with structLiteral is not wether they are Lvalue or not it's rather that when they are they are not modifiable Lvalues.

fail_compilation/b20011.d(31): Error: `U1(cast(ubyte)0u, ).m2` is not an lvalue and cannot be modified
fail_compilation/b20011.d(35): Error: function `b20011.main.assignableByRef(ref ubyte p)` is not callable using argument types `(ubyte)`
fail_compilation/b20011.d(35): cannot pass rvalue argument `S1(cast(ubyte)0u).member` of type `ubyte` to parameter `ref ubyte p`
fail_compilation/b20011.d(36): Error: function `b20011.main.assignableByOut(out ubyte p)` is not callable using argument types `(ubyte)`
Copy link

Choose a reason for hiding this comment

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

The messages in my PR are better. Yours don't mention anything about constness and are rather confusing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I totally disagree.

Copy link

Choose a reason for hiding this comment

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

The test cases are in now. Free to you to rework but I still think that it's sub-accurate to just output that the func cant be called. The user would have to think more about the why.

Return false for fields of struct literals.
Which just patched over some cracks wrt. accessing fields of struct
literals. Not a full revert, as the (useful) test cases are kept.
@kinke
Copy link
Contributor Author

kinke commented Sep 25, 2020

Rebased once more...

Copy link
Member

@ibuclaw ibuclaw left a comment

Choose a reason for hiding this comment

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

Let's go, as ICE's would also be present in gdc-master due to bad AST sent from the front-end.

@dlang-bot dlang-bot merged commit a0875a7 into dlang:master Sep 25, 2020
@kinke kinke deleted the fix19754 branch September 25, 2020 11:47
@Geod24
Copy link
Member

Geod24 commented Oct 15, 2020

This broke our code. Reduced test case:

public T deserializeFull (T) () @safe
{
    static if (is(T : E*, E))
    {
        return &[ deserializeFull!(typeof(T.init[0]))() ][0];
    }
    return T.init;
}

struct Block { string value; }

void main ()
{
    auto b = deserializeFull!(immutable(Block)*)();
}

Here, the literal is a GC allocation, so why is the compiler refusing it ? Moreover, I'm asking for an immutable value (originally was asking for const, doesn't make a difference), so the error message is at least misleading.

@ibuclaw
Copy link
Member

ibuclaw commented Oct 15, 2020

This broke our code. Reduced test case:

Thanks, can we have a regression issue?

@Geod24
Copy link
Member

Geod24 commented Oct 15, 2020

Sure. Just wanted to confirm if that was intended. Anyway, opened https://issues.dlang.org/show_bug.cgi?id=21312

@ibuclaw
Copy link
Member

ibuclaw commented Oct 15, 2020

Sure. Just wanted to confirm if that was intended. Anyway, opened https://issues.dlang.org/show_bug.cgi?id=21312

It may be intended, but I think is better to digress in a bug report.

@kinke
Copy link
Contributor Author

kinke commented Oct 15, 2020

This is intended. People seem to have abused this [<rvalue>][0] to turn an rvalue into an lvalue, surely many times without realizing that this entails creating a GC array, e.g., #10124 (comment).

@kinke
Copy link
Contributor Author

kinke commented Oct 15, 2020

It might be nice to have a little moveToGC helper in core.memory or so, for something like this without array overhead:

extern (C) void* _d_newitemU(in TypeInfo _ti);

T* moveToGC(T)(auto ref T value)
{
    import core.lifetime;
    auto mem = cast(T*) _d_newitemU(typeid(T)); // allocate without initializing
    moveEmplace(value, *mem);
    return mem;
}

public T deserializeFull (T) () @safe
{
    static if (is(T : immutable E*, E))
        return () @trusted { return cast(T) deserializeFull!E.moveToGC(); }();
    else
        return T.init;
}

@Geod24
Copy link
Member

Geod24 commented Oct 15, 2020

Well I'd be happy to use [ ... ].ptr, unfortunately someone decided it wasn't @safe.

@kinke
Copy link
Contributor Author

kinke commented Oct 15, 2020

I guess that would be fixable for array literals with at least one element, but I'd still consider [<elem>].ptr obscure and an explicit moveToGC() much better, even without considering the array overhead.

@Geod24
Copy link
Member

Geod24 commented Oct 15, 2020

but I'd still consider [].ptr obscure and an explicit moveToGC() much better, even without considering the array overhead.

"I consider this syntax obscure" is not reason enough to break the language. If it was, I would have nuked with statements from orbit :)
The code in question is in a serializer. Using &[ ... ][0] worked as intended, and now it does not, for what seems to be an arbitrary limitation. Worse it comes without a deprecation. Where is the benefit to the user ?

@kinke
Copy link
Contributor Author

kinke commented Oct 15, 2020

That comment was wrt. your code example, not in general.

The benefit IMO is consistency. Indexing or dot-var'ing a literal now consistently yields an rvalue, array literals being no exception. My thinking was that a part of a literal is still a literal. E.g., this used to compile too (without @safe) and was the reason for the Ocean issue IIRC:

struct S { int x; }
void main() { auto p = &S(0).x; *p = 123; }

IIRC (it's been a while), NOT doing this would lead to compile errors with master not being caught by the compiler anymore after cast-related changes/fixes in earlier commits.

kinke added a commit to kinke/druntime that referenced this pull request Oct 15, 2020
As hopefully idiomatic alternative to the `&[value][0]` 'pattern',
which doesn't compile anymore since dlang/dmd#10124.
kinke added a commit to kinke/druntime that referenced this pull request Oct 15, 2020
As hopefully idiomatic alternative to the `&[value][0]` 'pattern',
which doesn't compile anymore since dlang/dmd#10124.
kinke added a commit to kinke/druntime that referenced this pull request Oct 15, 2020
As hopefully idiomatic alternative to the `&[value][0]` 'pattern',
which doesn't compile anymore since dlang/dmd#10124.
@Geod24
Copy link
Member

Geod24 commented Oct 16, 2020

The benefit IMO is consistency. Indexing or dot-var'ing a literal now consistently yields an rvalue, array literals being no exception.

I would rather have the compiler be consistent on the whole expression than just two levels of it.
The full expression includes a & which should not do this optimization unless there's also a dereference in the same expression, or the compiler has another mean to prove the address doesn't escape.

My thinking was that a part of a literal is still a literal.

Right, but [] has always returned an lvalue. If we can optimize it to sometimes return rvalues it's great, but the current state seems problematic.
For example, what about:

const foo = [ 42, 24 ];

If we want to be consistent, shouldn't this give us a const int[2] ?

And yeah, the code you liked is obviously wrong, and I'm glad you put so much effort into fixing this. I just think we can do this fix while still retaining the old behavior when someone takes the address of an array literal.

@kinke
Copy link
Contributor Author

kinke commented Oct 16, 2020

I gave this some more thought yesterday. I do see that a (dynamic) array literal is a special case, and while the slice itself is an rvalue, its elements clearly are not. On the other hand, emplacing a single element in a new GC-allocation is the only use case I can think of for immediately indexing an array literal, and that comes with associated array overhead (it will never be appended to, as there's no reference to the array literal anymore when immediately indexing it). So I think moveToGC() is better for this task, but comes with a limitation, as moving means the source value must be mutable.

I can open a new PR treating array literal elements as lvalues again, to check the effect on the compile errors.

kinke added a commit to kinke/druntime that referenced this pull request Dec 6, 2020
As hopefully idiomatic alternative to the `&[value][0]` 'pattern',
which doesn't compile anymore since dlang/dmd#10124.
kinke added a commit to kinke/druntime that referenced this pull request Dec 13, 2020
As hopefully idiomatic alternative to the `&[value][0]` 'pattern',
which doesn't compile anymore since dlang/dmd#10124.
dlang-bot pushed a commit to dlang/druntime that referenced this pull request Dec 30, 2020
As hopefully idiomatic alternative to the `&[value][0]` 'pattern',
which doesn't compile anymore since dlang/dmd#10124.
n8sh pushed a commit to n8sh/druntime that referenced this pull request Mar 3, 2021
As hopefully idiomatic alternative to the `&[value][0]` 'pattern',
which doesn't compile anymore since dlang/dmd#10124.
@JohanEngelen
Copy link
Contributor

This introduced regression https://issues.dlang.org/show_bug.cgi?id=23273

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.