-
Notifications
You must be signed in to change notification settings - Fork 3
Description
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:
- 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
- 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.