Skip to content

Conversation

dukc
Copy link
Contributor

@dukc dukc commented May 1, 2018

This is #6178 rebooted, so most of this is work of @timotheecour , not mine. It tries to address the last review @andralex made of it, and also some feedback made to #6214, which is my pull request aiming for same functionality.

@dlang-bot
Copy link
Contributor

Thanks for your pull request and interest in making D better, @dukc! 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 annotated coverage diff directly on GitHub with CodeCov's browser extension
  • 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
16745 enhancement Add template helper for creating static arrays with the size inferred

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 "master + phobos#6489"

@dukc dukc force-pushed the staticArray branch 3 times, most recently from 35d43c7 to 8ac17a6 Compare May 1, 2018 17:45
@dukc
Copy link
Contributor Author

dukc commented May 2, 2018

Circleci crashed. Trying to restart...

@dukc dukc closed this May 2, 2018
@dukc dukc reopened this May 2, 2018
@dukc
Copy link
Contributor Author

dukc commented May 2, 2018

Anybody know how I can fix the Circleci seqfault?

@wilzbach
Copy link
Contributor

wilzbach commented May 2, 2018

Unfortunately that's a known issue: https://issues.dlang.org/show_bug.cgi?id=18667

(I just restarted the CI as it only happens in ~ every 20-30 run)

@wilzbach
Copy link
Contributor

wilzbach commented May 2, 2018

Oh and you can only restart CircleCi by either

  • being a member of the D GitHub organization and then logging in directly at their page
  • git pushes - an empty forced pushed with git commit --amend helps

@dukc dukc force-pushed the staticArray branch 4 times, most recently from 91bfbaf to 8ecb0d2 Compare May 2, 2018 06:58
Copy link
Contributor

@wilzbach wilzbach left a comment

Choose a reason for hiding this comment

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

Not sure about the rangeLength out parameter. Is there a good use case for it?

std/array.d Outdated
}

/// ditto
auto staticArray(size_t n, T)(T a, out size_t rangeLength)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need rangeLength? (i.e. is there any good use case?)

Copy link
Contributor Author

@dukc dukc May 2, 2018

Choose a reason for hiding this comment

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

Andrei wanted that feature in #6214 review, and because it's so similar I assume the same wish applies here too.

std/array.d Outdated
return theArray;
}());

for (auto iter = a.take(n); !iter.empty; iter.popFront())
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not foreach?

std/array.d Outdated
{
// Compile-time version to avoid unchecked memory access.
Unqual!U[n] ret;
for (auto iter = a.take(n); !iter.empty; iter.popFront())
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not foreach?

Copy link
Contributor Author

@dukc dukc May 2, 2018

Choose a reason for hiding this comment

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

It was a ref foreach in #6178, but Andrei pointed out that it woud not work with rvalue elements, and suggested this. Would foreach without ref make a needless copy with lvalue elements? Not sure.

return (() @trusted => cast(U[n]) ret)();
}

auto ret = (() @trusted
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be @trusted?

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 think removing @trusted would prevent the function from being safe if the element type is some pointer. Because they are initialized later, the function should be safe with pointers too.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah right. Forgot that using void makes every unsafe. Sorry.

std/array.d Outdated

Returns: A static array constructed from `a`.
+/

Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: we typically don't have an empty line here.

@dukc
Copy link
Contributor Author

dukc commented May 8, 2018

/cc @andralex still wanted a final review on the issue.

Copy link
Member

@andralex andralex left a comment

Choose a reason for hiding this comment

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

Getting there. Exceptions safety still not present.

std/array.d Outdated
}

pragma(inline, true) U[n] staticArray(U, T, size_t n)(auto ref T[n] a) nothrow @safe pure @nogc
if (!is(T == U) && is(T : U))
Copy link
Member

Choose a reason for hiding this comment

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

Do we need the first test? is(T : U) should be sufficient.

Copy link
Contributor Author

@dukc dukc May 15, 2018

Choose a reason for hiding this comment

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

Aye. Tried without, there was ambiquity with the first overload in this pr.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, indeed. I missed the negation on the first is...

std/array.d Outdated

for (auto iter = a.take(n); !iter.empty; iter.popFront())
{
emplaceRef!U(ret[i++], iter.front);
Copy link
Member

Choose a reason for hiding this comment

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

If an exception is thrown, we're toast - uninitialized values will be incorrectly destroyed. See @timotheecour 's PR where I posted code that can be used to fix that.

@dukc
Copy link
Contributor Author

dukc commented May 15, 2018

@andralex exceptions and destructor thing addressed.

Good catch btw! I think no-one else noticed.

Copy link
Member

@andralex andralex left a comment

Choose a reason for hiding this comment

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

one more look pls

std/array.d Outdated
}

pragma(inline, true) U[n] staticArray(U, T, size_t n)(auto ref T[n] a) nothrow @safe pure @nogc
if (!is(T == U) && is(T : U))
Copy link
Member

Choose a reason for hiding this comment

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

Oh, indeed. I missed the negation on the first is...

std/array.d Outdated

/// ditto
auto staticArray(Un : U[n], U, size_t n, T)(T a) nothrow
if ((isInputRange!T) && is(ElementType!T : U))
Copy link
Member

Choose a reason for hiding this comment

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

redundant parens

std/array.d Outdated
}

/// ditto
auto staticArray(Un : U[n], U, size_t n, T)(T a) nothrow
Copy link
Member

Choose a reason for hiding this comment

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

wait can't this throw if copying objects throws?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed. That's a leftover from Timothee. I think I also need to add a test to prevent errors like this.

std/array.d Outdated

/// ditto
auto staticArray(Un : U[n], U, size_t n, T)(T a, out size_t rangeLength)
if ((isInputRange!T) && is(ElementType!T : U))
Copy link
Member

Choose a reason for hiding this comment

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

redundant parens

@dukc
Copy link
Contributor Author

dukc commented May 16, 2018

@andralex "Looked" once more.

@dukc
Copy link
Contributor Author

dukc commented May 16, 2018

Btw the previous version did not compile with elaborate destructors, the default-initializator was incorrectly written. But tests didn't catch that because of the static if.

I solved that by putting the same code that default-initializes the rest of the array when the range is too short in scope (exit), so that it should now handle the exceptions as well.

@dukc
Copy link
Contributor Author

dukc commented May 16, 2018

@wilzbach CircleCI fails to compile but my local compiler and the autotester do not. What is happening?

@wilzbach
Copy link
Contributor

The auto-tester doesn't test with -dip1000, only CircleCi does. You can check locally with the std/array.test target

@dukc
Copy link
Contributor Author

dukc commented May 16, 2018

Understood. I have to look at that later.

std/array.d Outdated
return theArray;
}());

if (true)
Copy link
Contributor

Choose a reason for hiding this comment

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

if (true)?

Copy link
Contributor Author

@dukc dukc May 19, 2018

Choose a reason for hiding this comment

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

It's for the scope(exit) inside it. I could use just a regular block statement but if (true) looks better to the eye IMO. Does the style guide say anything about this?

std/array.d Outdated
import std.range : take;
import std.conv : emplaceRef;

size_t i;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd declare these is locally next to the loops to make it clearer what the scope is. Stylistic choice, though.

template argument to avoid having to specify the number of elements
(eg: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`).

Note: $(D fun([1, 2, 3].staticArray)) may be inefficient because of the copies involved.
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we be able to make sure that RVO happens?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ask Walter, not me. Because the comment was here already I thought there may be a reason for it and decided to leave it there..

Copy link
Contributor

Choose a reason for hiding this comment

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

I would remove the note from the changelog as I think it's clear that in the general case static arrays need to be copied and such details belong into the detailed documentation of the function.

See also: https://godbolt.org/g/GEsh3E

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, i didn't notice that static array was being fed to a function. I thought he meant that [1, 2, 3] might be unnecessarily copied when initializing. Sounded unlikely, but I thought that Timothee might know something I do not.

But since it means that feeding a static array to a function involves a copy, I will remove that.

But looking at that godbolt example, why does LDC make bar to copy the static array when passing it to foo when it does not use it for anything else? Can't it rely on foo to copy it if it mutates it?

std/array.d Outdated
@system nothrow unittest
{
// exists only to allow doing something in the destructor
static int preventersDestroyed = 0;
Copy link
Contributor

@dnadlinger dnadlinger May 18, 2018

Choose a reason for hiding this comment

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

Shouldn't this be compared against the expected value later?

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 originally did, but it was larger than I excepted. That tells me that the value is implementatation-dependant and thus better to not be tested.

Copy link
Contributor Author

@dukc dukc May 19, 2018

Choose a reason for hiding this comment

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

However I decided to add assert (false) after the static array initializer to ensure the copy preventing postblit works, and also added the above rationale to a comment.

std/array.d Outdated

nothrow pure @safe unittest
{

Copy link
Contributor

Choose a reason for hiding this comment

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

Superfluous whitespace.

std/array.d Outdated
{
// NOTE: without `cast`, getting error:
// cannot deduce function from argument types !(length)(Result)
return .staticArray!(cast(size_t) a.length)(a);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be size_t(a.length) to avoid accidentally introducing bugs?

@dukc dukc force-pushed the staticArray branch 2 times, most recently from 2d7125f to 0eedb3c Compare May 19, 2018 05:44
@dukc
Copy link
Contributor Author

dukc commented May 19, 2018

Thanks for the comments. All explained and/or fixed.

@dukc dukc force-pushed the staticArray branch 3 times, most recently from ed55a66 to 0fd213f Compare May 19, 2018 10:55
@dukc
Copy link
Contributor Author

dukc commented May 22, 2018

@andralex Tests pass and it's ready for another try at review.

@dukc
Copy link
Contributor Author

dukc commented May 23, 2018

ping @andralex

template argument to avoid having to specify the number of elements
(eg: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`).

Note: $(D fun([1, 2, 3].staticArray)) may be inefficient because of the copies involved.
Copy link
Contributor

Choose a reason for hiding this comment

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

I would remove the note from the changelog as I think it's clear that in the general case static arrays need to be copied and such details belong into the detailed documentation of the function.

See also: https://godbolt.org/g/GEsh3E

std/array.d Outdated
}

[cast(byte) 1, cast(byte) 129].staticArray.checkStaticArray!byte([1, -127]);

Copy link
Contributor

Choose a reason for hiding this comment

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

unnecessary empty line.

}

/// ditto
auto staticArray(size_t n, T)(scope T a, out size_t rangeLength)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still not sure whether it's a good idea to expose the rangeLength API if there's no good use case for it. Or did you manage to find one?

Copy link
Contributor Author

@dukc dukc Jun 9, 2018

Choose a reason for hiding this comment

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

I believe it was wanted for cases like reading input to a fixed-size buffer. So that one can get the length of the input actually in the buffer without using tee or enumerate.

See: #6178 (comment) and #6214 (review)

@dukc
Copy link
Contributor Author

dukc commented Jun 9, 2018

Okay, addressed.

@dukc
Copy link
Contributor Author

dukc commented Jun 15, 2018

ping @andralex

@wilzbach wilzbach dismissed andralex’s stale review June 19, 2018 00:19

review has been addressed.

Copy link
Contributor

@wilzbach wilzbach left a comment

Choose a reason for hiding this comment

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

A few final nits.
@dukc would you mind squashing this to one commit afterwards, s.t. we can finally move on?
Unfortunately we missed 2.081, but I really want to see this in 2.082

Added `staticArray` to construct a static array from input array / range / CT range.

The type of elements can be specified implicitly ($(D [1, 2].staticArray) of type `int[2]`)
or explicitly ($(D [1, 2].staticArray!float) of type `float[2]`).
Copy link
Contributor

Choose a reason for hiding this comment

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

"of" sounds a bit confusing, maybe "will result in an array of type", "results type", "creates type" or "yields"?


The type of elements can be specified implicitly ($(D [1, 2].staticArray) of type `int[2]`)
or explicitly ($(D [1, 2].staticArray!float) of type `float[2]`).
When `a` is a range with length not known at compile time, the number of elements must be given as template argument
Copy link
Contributor

Choose a reason for hiding this comment

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

s/with/whose/

The type of elements can be specified implicitly ($(D [1, 2].staticArray) of type `int[2]`)
or explicitly ($(D [1, 2].staticArray!float) of type `float[2]`).
When `a` is a range with length not known at compile time, the number of elements must be given as template argument
(eg `myrange.staticArray!2`).
Copy link
Contributor

Choose a reason for hiding this comment

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

s/eg/e.g./

or explicitly ($(D [1, 2].staticArray!float) of type `float[2]`).
When `a` is a range with length not known at compile time, the number of elements must be given as template argument
(eg `myrange.staticArray!2`).
Size and type can be combined, if source range elements are implicitly
Copy link
Contributor

Choose a reason for hiding this comment

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

if the source range elements?

convertible to the requested element type (eg: `2.iota.staticArray!(long[2])`).
When the range `a` is known at compile time, it can also be specified as a
template argument to avoid having to specify the number of elements
(eg: `staticArray!(2.iota)` or `staticArray!(double, 2.iota)`).
Copy link
Contributor

Choose a reason for hiding this comment

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

s/eg/e.g./

std/array.d Outdated
Size and type can be combined (eg: `2.iota.staticArray!(byte[2])`).
When the range `a` is known at compile time, it can also be specified as a
template argument to avoid having to specify the number of elements
(eg: `staticArray!(2.iota)` or $(D staticArray!(double, 2.iota))).
Copy link
Contributor

Choose a reason for hiding this comment

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

s/eg/e.g./

std/array.d Outdated
/++
Constructs a static array from `a`.
The type of elements can be specified implicitly ($(D [1,2].staticArray) of type `int[2]`)
or explicitly ($(D [1,2].staticArray!float) of type `float[2]`).
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment about "of"

std/array.d Outdated
Constructs a static array from `a`.
The type of elements can be specified implicitly ($(D [1,2].staticArray) of type `int[2]`)
or explicitly ($(D [1,2].staticArray!float) of type `float[2]`).
When `a` is a range with length not known at compile time, the number of elements must be given as a template argument
Copy link
Contributor

Choose a reason for hiding this comment

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

s/with/whose/

std/array.d Outdated
template argument to avoid having to specify the number of elements
(eg: `staticArray!(2.iota)` or $(D staticArray!(double, 2.iota))).

Note: `staticArray` returns by value, so expressions involving large arrays may be inefficient.
Copy link
Contributor

Choose a reason for hiding this comment

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

We already agreed on removing this from the changelog. I think the same argumentation applies for the docs too?

@dukc
Copy link
Contributor Author

dukc commented Jun 21, 2018

@wilzbach Docs corrected. But I don't want to squash, because I don't know how to squash commits by others. I don't want to accidently remove all credit to Timothee!

I let you to squash them, if you want. But please do not remove Timothee.

@wilzbach
Copy link
Contributor

But I don't want to squash, because I don't know how to squash commits by others. I don't want to accidently remove all credit to Timothee!
I let you to squash them, if you want. But please do not remove Timothee.

Okay squashed them down to two commits (i.e. all your commits into one and commits of Timothee into one commit).

@wilzbach
Copy link
Contributor

Auto-merge toggled on

@wilzbach wilzbach merged commit 90a8fc3 into dlang:master Jun 30, 2018
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.

6 participants