Skip to content

Range initialization expression evaluated twice after range-based for transformation #3

@muggenhor

Description

@muggenhor

Looking at the code at ReplaceForRange.inc:69 you seem to be jumping through extra hoops to eliminate the intermediate __range variable from being emitted.

This is non-conformant for two reasons:

  1. You're no longer applying lifetime extension to the object returned by the range-init expression (section 12.2 [class.temporary], paragraph 5) and thus not ensuring that said object is still alive to iterate over after having extracted its iterators
  2. You're now evaluating the range-init expression twice, which is a problem if that expression is not idempotent. I.e. it might be possible for the second evaluation of the range-init expression to return a different object, and thus the two iterators would be part of different ranges and comparing them be meaningless. This is true for mostly every range-init expression that's an prvalue (e.g. any function returning a newly constructed range object).

This could be solved by emitting the equivalent of the following instead (C++17 spec, 6.5.4 [stmt.ranged]):

{
  auto&& __range = <for-range-initializer>;
  auto __begin = &__range[0] <or> __range.begin() <or> begin(__range);
  auto __end  = &__range[sizeof(__range)/sizeof(__range[0])] <or> __range.end() <or> end(__range);
  for (; __begin != __end; ++__begin)
  {
    <for-range-declaration> = *__begin;
    <for-statement>
  }
}

Note that you're almost there already. I believe, from code inspection (haven't yet had the time to try), that all you need to do is to emit the C++03 equivalent of auto&& __range = <for-range-initializer> preceding its use instead of replacing the uses of __range with <for-range-initializer>.

Edit: if a universal reference (auto&&) is a problem: due to the above description of the usage of __range I believe an l-value reference (auto& __range = ...) would suffice as well, because no std::forward<decltype(__range)>-like constructs are used.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions