This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Open status.

### 2708. `recursive_directory_iterator::recursion_pending()` is incorrectly specified

Section: 29.12.12.2 [fs.rec.dir.itr.members] Status: Open Submitter: Eric Fiselier Opened: 2016-05-09 Last modified: 2018-08-24

Priority: 2

View all other issues in [fs.rec.dir.itr.members].

View all issues with Open status.

Discussion:

The current specification of `recursion_pending()` says (29.12.12.2 [fs.rec.dir.itr.members]/24):

> Returns: `true` if `disable_recursion_pending()` has not been called subsequent to the prior construction or increment operation, otherwise `false`.

This language does not take into account cases where the prior construction was a copy construction from a iterator, `it`, where `it.recursion_pending() == false`.

[2016-08 Chicago]

Wed AM: Move to Open

[2018-1-26 issues processing telecon]

Status to 'Tentatively Ready'; Casey will explore whether making `recursion_pending` an exposition-only member makes this clearer.

Previous resolution from Eric [SUPERSEDED]:

> This wording is relative to N4582.

> 1. Change 29.12.12.2 [fs.rec.dir.itr.members] as indicated:

>    >     explicit recursive_directory_iterator(const path& p);
>    >     recursive_directory_iterator(const path& p, directory_options options);
>    >     recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
>    >     recursive_directory_iterator(const path& p, error_code& ec) noexcept;

>    > > […]

>    > > -3- Postcondition: `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

>    > > - `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

>    > > - `recursion_pending() == true`.

>    > > […]

>    > > [Drafting note: The following changes the specification of `recursion_pending()` seemingly recursive. Perhaps it would be easier to specify `recursion_pending()` in terms of a exposition only member in `recursive_directory_iterator`.]

>    >     bool recursion_pending() const;

>    > > […]

>    > > -24- Returns: `true` if `disable_recursion_pending()` has not been called subsequent to the prior construction or increment operation, otherwise `false` `false` if `disable_recursion_pending()` has been called subsequent to the prior construction or increment operation, otherwise the value of `recursion_pending()` set by that operation.

>    > > […]

>    >     recursive_directory_iterator& operator++();
>    >     recursive_directory_iterator& increment(error_code& ec) noexcept;

>    > > […]

>    > > -27- Effects: As specified by Input iterators (24.2.3), except that: […]

>    > > -?- Postcondition: `recursion_pending() == true`.

[2018-01-29: Casey provides a PR with an exposition-only member]

Status to 'Review'.

Previous resolution from Casey [SUPERSEDED]:

> This wording is relative to N4713.

> 1. Change [fs.rec.dir.itr] as indicated:

>    >         […]

>    >         // other members as required by 23.3.5.3 [input.iterators], input iterators

>    >       private:
>    >         bool recurse_; // exposition-only
>    >       };
>    >     }

> 2. Change 29.12.12.2 [fs.rec.dir.itr.members] as indicated:

>    >     explicit recursive_directory_iterator(const path& p);
>    >     recursive_directory_iterator(const path& p, directory_options options);
>    >     recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
>    >     recursive_directory_iterator(const path& p, error_code& ec) noexcept;

>    > > […]

>    > > -3- Postconditions: `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

>    > > - `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

>    > > - `recurse_ == true`.

>    > > […]

>    >     recursive_directory_iterator(const recursive_directory_iterator& rhs);

>    > > […]

>    > > -8- Postconditions:

>    > > […]

>    > > (8.3) — `recursion_pending() == rhs.recursion_pending()``recurse_ == rhs.recurse_`

>    >     recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;

>    > > […]

>    > > -10- Postconditions: `options()`, `depth()`, and `recursion_pending()``recurse_` have the values that `rhs.options()`, `rhs.depth()`, and `rhs.recursion_pending()``rhs.recurse_`, respectively, had before the function call.

>    >     recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);

>    > > […]

>    > > -12- Postconditions:

>    > > […]

>    > > (12.3) — `recursion_pending() == rhs.recursion_pending()``recurse_ == rhs.recurse_`

>    > > […]

>    >     recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;

>    > > […]

>    > > -15- Postconditions: `options()`, `depth()`, and `recursion_pending()``recurse_` have the values that `rhs.options()`, `rhs.depth()`, and `rhs.recursion_pending()``rhs.recurse_`, respectively, had before the function call.

>    > > […]

>    >     bool recursion_pending() const;

>    > > -21- Returns: `true` if `disable_recursion_pending()` has not been called subsequent to the prior construction or increment operation, otherwise `false` recurse_.

>    > > […]

>    >     recursive_directory_iterator& operator++();
>    >     recursive_directory_iterator& increment(error_code& ec) noexcept;

>    > > -23- Effects: As specified for the prefix increment operation of Input iterators ( [iterators.input]), except that:

>    > > […]

>    > > -?- Postcondition: `recurse_ == true`.

>    >     void disable_recursion_pending();

>    > > -28- Postconditions: `recursion_pending()recurse_ == false`.

>    > > […]

[2018-05-23: Casey restores the intended design with an expansion of the original PR]

The intended design is that all copies of a single `recursive_directory_iterator` share a common block of state which includes the values returned by `options`, `depth`, and `recursion_pending` - hence the mandate that those functions not be called on a non-dereferenceable iterator in 29.12.12 [fs.class.rec.dir.itr] para 2. To allow an implementation with such shared state, it's necessary to make changes to the value returned by `recursion_pending()` visible to all copies of the same dereferenceable iterator.

Also:

- `pop` notionally calls `increment` repeatedly until the current directory is exhausted, `pop` should affect the value of `recursion_pending` similarly to `increment`.

- `options` is not valid for all constructor signatures described by 29.12.12.2 [fs.rec.dir.itr.members] para 2.

- the copies and moves don't specify what they actually do

- it's not quite kosher for the copies and moves to have postconditions on the value of expressions that have UB if the iterator copied/moved from is not dereferenceable.

[2018-06, Rapperswil, Wednesday evening]

JW: p21 currently can just say "unspecified"
BO: if we are OK with only remote implementations we can remove the unspecifiedness
BO: the problematic business is the "recursion pending" bit
JW: I want time to work on this

Move to open and note that Jonathan is reviewing and making recommendations.

[2018-08-23 Batavia Issues processing]

General agreement that flag should be shared; Casey to reword.

Proposed resolution:

This wording is relative to N4750.

1. Change 29.12.12.2 [fs.rec.dir.itr.members] as indicated:

   >     explicit recursive_directory_iterator(const path& p);
   >     recursive_directory_iterator(const path& p, directory_options options);
   >     recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
   >     recursive_directory_iterator(const path& p, error_code& ec) noexcept;

   > > -?- For the signatures with no parameter `options`, let `options` be `directory_options::none`.

   > > -2- Effects: […]

   > > -3- Postconditions: `options() == options` for the signatures with a `directory_options` argument, otherwise `options() == directory_options::none`.

   > > - `this->options() == options`

   > > - `recursion_pending() == true`

   > > […]

   >     recursive_directory_iterator(const recursive_directory_iterator& rhs);

   > > -7- Effects: Constructs an object of class `recursive_directory_iterator` iterator that denotes the same directory entry as `rhs`, if any..

   > > -8- Postconditions: If `rhs` is dereferenceable,

   > > […]

   >     recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;

   > > -9- Effects: Constructs an object of class `recursive_directory_iterator` iterator that denotes the directory entry denoted by `rhs` before the function call, if any..

   > > -10- Postconditions: If `rhs` is dereferenceable, […]

   >     recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);

   > > -11- Effects: If `*this` and `rhs` are the same object, the member has no effect. Causes `*this` to denote the same directory entry denoted by `rhs`, if any.

   > > -12- Postconditions: If `rhs` is dereferenceable,

   > > […]

   >     recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;

   > > -14- Effects: If `*this` and `rhs` are the same object, the member has no effect. Causes `*this` to denote the directory entry denoted by `rhs` before the function call, if any.

   > > -15- Postconditions: If `rhs` was dereferenceable before the function call, […]

   > > -16- Returns: `*this`.

   > > -x- Remarks: If `*this` and `rhs` do not refer to the same object, the resulting state of `rhs` is unspecified (16.4.6.15 [lib.types.movedfrom]).

   >     directory_options options() const;

   > > -17- Returns: The value of the argument passed to the constructor for the `options` parameter, if present, otherwise `directory_options::none` established by the most recently called member that has a postcondition for `options()`.

   > > […]

   >     bool recursion_pending() const;

   > > -21- Returns: `true` if `disable_recursion_pending()` has not been called subsequent to the prior construction or increment operation, otherwise `false`. If `disable_recursion_pending()` has been called on a copy of `*this`, an unspecified value. Otherwise, the value established for `recursion_pending()` by the postcondition of the most recent construction, assignment, increment, or `disable_recursion_pending` operation.

   > > […]

   >     recursive_directory_iterator& operator++();
   >     recursive_directory_iterator& increment(error_code& ec);

   > > -23- Effects: As specified for the prefix increment operation of Input iterators (23.3.5.3 [input.iterators]), except that: […]

   > > -?- Postconditions: If `*this` is dereferenceable, `recursion_pending() == true`.

   > > […]

   >     void pop();
   >     void pop(error_code& ec);

   > > -26- Effects: If `depth() == 0`, set `*this` to `recursive_directory_iterator()`. […]

   > > -?- Postconditions: If `*this` is dereferenceable, `recursion_pending() == true`.

   > > […]