This is an archive of the discontinued LLVM Phabricator instance.

[DebugInfo] Have custom std::reverse_iterator<DWARFDie>
ClosedPublic

Authored by JDevlieghere on Jul 23 2018, 10:22 AM.

Diff Detail

Repository
rL LLVM

Event Timeline

JDevlieghere created this revision.Jul 23 2018, 10:22 AM

What's the reason for not specializing std::reverse_iterator? AFAIK, that is officially permitted by the standard.

This way somebody can still construct a std::reverse_iterator<DWARFDie::iterator> (via make_reverse_iterator or whatever) and get UB.

What's the reason for not specializing std::reverse_iterator? AFAIK, that is officially permitted by the standard.

What exactly would you do difference? I can see a solution where we make the current __tmp a class member of the iterator adapter, but maybe you have something more straightforward in mind?

This way somebody can still construct a std::reverse_iterator<DWARFDie::iterator> (via make_reverse_iterator or whatever) and get UB.

That's not entirely true because it's no longer a bidirectional iterator. Regardless of the specialization, both *++iterator and *--iterator will remain UB.

What's the reason for not specializing std::reverse_iterator? AFAIK, that is officially permitted by the standard.

What exactly would you do difference? I can see a solution where we make the current __tmp a class member of the iterator adapter, but maybe you have something more straightforward in mind?

This way somebody can still construct a std::reverse_iterator<DWARFDie::iterator> (via make_reverse_iterator or whatever) and get UB.

That's not entirely true because it's no longer a bidirectional iterator. Regardless of the specialization, both *++iterator and *--iterator will remain UB.

Sorry I wrote the reply without looking at the patch in detail. I did not notice you demoted the iterator back to "forward". In case this is not a bidirectional operator, but a collection of two forward iterators that happen to iterate in the reverse order, the specializing std::reverse_iterator is not the right approach.

I have to admit I haven't thought about the specifics of how the reverse iterator would work until now. However, now that I think about it, I think it could be implemented as a pair of DWARFDie + bool flag. The trick would be that we store the element that we really dereference to, instead of its successor. The bool flag would be used denote the before_begin state, as we cannot represent rend() this way. The implementation would do something like:

operator++() {
  assert(!AtEnd && "Incrementing rend");
  DWARFDie D = Die.getPreviousSibling();
  if (D)
    Die = D;
  else
    AtEnd = true;
}

operator--() {
  if (AtEnd) {
    AtEnd = false;
    return;
  }
  Die = Die.getNextSibling();
  assert(!Die.isNULL() && "Decrementing rbegin");
}

However, if you don't need that extra complexity now, then I suppose this solution is fine too.

llvm/unittests/DebugInfo/DWARF/DWARFDebugInfoTest.cpp
1081–1116 ↗(On Diff #156820)

This will succeed even if the iterator range returns an empty sequence.

Might I suggest a shorter alternative with a more helpful error message?

EXPECT_THAT(std::vector<DWARFDie>(A.begin(), A.end()),
            testing::ElementsAre(B, C, D));
  • Feedback Pavel

Sorry I wrote the reply without looking at the patch in detail. I did not notice you demoted the iterator back to "forward". In case this is not a bidirectional operator, but a collection of two forward iterators that happen to iterate in the reverse order, the specializing std::reverse_iterator is not the right approach.

I have to admit I haven't thought about the specifics of how the reverse iterator would work until now. However, now that I think about it, I think it could be implemented as a pair of DWARFDie + bool flag. The trick would be that we store the element that we really dereference to, instead of its successor. The bool flag would be used denote the before_begin state, as we cannot represent rend() this way. The implementation would do something like:

operator++() {
  assert(!AtEnd && "Incrementing rend");
  DWARFDie D = Die.getPreviousSibling();
  if (D)
    Die = D;
  else
    AtEnd = true;
}

operator--() {
  if (AtEnd) {
    AtEnd = false;
    return;
  }
  Die = Die.getNextSibling();
  assert(!Die.isNULL() && "Decrementing rbegin");
}

I think I'm missing something; what are you implementing here exactly and what problem is it trying to solve?

However, if you don't need that extra complexity now, then I suppose this solution is fine too.

  • Specialize std::reverse_iterator for llvm::DWARFDie as suggested by Pavel.

I like this, but that's not surprising, as it was my idea in the first place. :)

I think David should have a pass at this too.

llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
373 ↗(On Diff #157026)

This will inject all llvm decls into the std namespace. bad idea :)

389–392 ↗(On Diff #157026)

These should be already inherited via std::iterator, shouldn't they?

410–418 ↗(On Diff #157026)

We should also add a test which does a manual reverse iteration (via this function) on the reverse_iterator :), to check that this works too.

420 ↗(On Diff #157026)

This should also take the value of AtEnd into account. Probably something like Die.isValid() && !AtEnd. Maybe you could even assert AtEnd == false when dereferencing.

Address Pavel's feedback:

  • Don't clutter std namespace.
  • Don't redefine iterator traits.
  • Add test cases that checks bidirectional aspect of both iterators.
  • Fix operator bool() to check AtEnd.
JDevlieghere retitled this revision from [DebugInfo] Have custom DWARFDie:: reverse_iterator to [DebugInfo] Have custom std::reverse_iterator<DWARFDie>.Jul 24 2018, 7:46 AM
dblaikie added inline comments.Jul 24 2018, 4:36 PM
llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
383–384 ↗(On Diff #157035)

Could a null DWARFDie (a default constructed one, rather than a non-default constructed one that points to a null DIE) be used as the 'rend' value - avoiding the need for the bool?

413–414 ↗(On Diff #157035)

Is this used/needed? Might be easier to leave it out & require users to do comparisons with the rend iterator?

420 ↗(On Diff #157035)

Any operator that can be a non-member should be, ideally (so op== and op!= for example), so that any implicit conversions can occur equally on the LHS and RHS of the expression.

labath added inline comments.Jul 25 2018, 12:59 AM
llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
383–384 ↗(On Diff #157035)

for operator-- you need to be able to go back from the "rend" position, which won't work for a default-constructed DWARFDie. I suppose that would be possible if we dropped operator-- (I don't expect anyone will need it), though then this will not be a conforming reverse_iterator.

JDevlieghere marked 8 inline comments as done.
  • Address Dave's feedback

(probably a separate patch) Wonder if we could have a specialization of llvm::reverse that detects whether the underlying range has 'rbegin/rend' functions and use those, rather than making reverse iterators out of end/begin. That way reverse(die) would work correctly.

llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
393–394 ↗(On Diff #157242)

/looks/ to me like this declaration isn't needed? I don't see any use of it in the specialization/before the op== definition later.

397–402 ↗(On Diff #157242)

Any chance of using llvm's iterator_facade here, might simplify things a bit?

447–456 ↗(On Diff #157242)

I wonder if these should go in the llvm namespace (they'd still be found by ADL, I think) rather than here in the std namespace?

  • Address Dave's feedback
JDevlieghere marked 3 inline comments as done.Jul 31 2018, 7:19 AM
dblaikie accepted this revision.Jul 31 2018, 3:04 PM
dblaikie added inline comments.
llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h
285–286 ↗(On Diff #158254)

extraneous/extra formatting?

295–324 ↗(On Diff #158254)

Looks like an unrelated change that can be committed separately?

435–436 ↗(On Diff #158254)

Could you be more precise - what goes wrong/what error is produced?

This revision is now accepted and ready to land.Jul 31 2018, 3:04 PM
This revision was automatically updated to reflect the committed changes.