blob: 21d96a6c01df682cce5b2f2f7d558e1aaa112118 [file] [log] [blame]
Edwin Vanee4e7c242013-03-09 03:33:50 +00001.. index:: Loop Convert Transform
2
3======================
4Loop Convert Transform
5======================
6
7The Loop Convert Transform is a transformation to convert ``for(...; ...;
8...)`` loops to use the new range-based loops in C++11. The transform is enabled
Chandler Carruthd9063c42013-09-04 17:35:07 +00009with the :option:`-loop-convert` option of :program:`clang-modernize`.
Edwin Vanee4e7c242013-03-09 03:33:50 +000010
11Three kinds of loops can be converted:
12
13- Loops over statically allocated arrays
14- Loops over containers, using iterators
15- Loops over array-like containers, using ``operator[]`` and ``at()``
16
17Risk
18====
19
Edwin Vanee4e7c242013-03-09 03:33:50 +000020Risky
Edwin Vane8d286462013-06-14 15:14:20 +000021-----
Edwin Vanee4e7c242013-03-09 03:33:50 +000022
23In loops where the container expression is more complex than just a
24reference to a declared expression (a variable, function, enum, etc.),
25and some part of it appears elsewhere in the loop, we lower our confidence
26in the transformation due to the increased risk of changing semantics.
27Transformations for these loops are marked as `risky`, and thus will only
28be converted if the acceptable risk level is set to ``-risk=risky``.
29
30.. code-block:: c++
31
32 int arr[10][20];
33 int l = 5;
34
35 for (int j = 0; j < 20; ++j)
36 int k = arr[l][j] + l; // using l outside arr[l] is considered risky
37
38 for (int i = 0; i < obj.getVector().size(); ++i)
39 obj.foo(10); // using 'obj' is considered risky
40
Edwin Vane3cb833d2013-03-26 16:44:29 +000041See
42:ref:`Range-based loops evaluate end() only once<IncorrectRiskyTransformation>`
43for an example of an incorrect transformation when the maximum acceptable risk
44level is set to `risky`.
45
Edwin Vanee4e7c242013-03-09 03:33:50 +000046Reasonable (Default)
Edwin Vane8d286462013-06-14 15:14:20 +000047--------------------
Edwin Vanee4e7c242013-03-09 03:33:50 +000048
49If a loop calls ``.end()`` or ``.size()`` after each iteration, the
50transformation for that loop is marked as `reasonable`, and thus will
51be converted if the acceptable risk level is set to ``-risk=reasonable``
52(default) or higher.
53
54.. code-block:: c++
55
56 // using size() is considered reasonable
57 for (int i = 0; i < container.size(); ++i)
58 cout << container[i];
59
60Safe
Edwin Vane8d286462013-06-14 15:14:20 +000061----
Edwin Vanee4e7c242013-03-09 03:33:50 +000062
63Any other loops that do not match the above criteria to be marked as
64`risky` or `reasonable` are marked `safe`, and thus will be converted
65if the acceptable risk level is set to ``-risk=safe`` or higher.
66
67.. code-block:: c++
68
69 int arr[] = {1,2,3};
70
71 for (int i = 0; i < 3; ++i)
72 cout << arr[i];
73
74Example
75=======
76
77Original:
78
79.. code-block:: c++
80
81 const int N = 5;
82 int arr[] = {1,2,3,4,5};
83 vector<int> v;
84 v.push_back(1);
85 v.push_back(2);
86 v.push_back(3);
87
88 // safe transform
89 for (int i = 0; i < N; ++i)
90 cout << arr[i];
91
92 // reasonable transform
93 for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
94 cout << *it;*
95
96 // reasonable transform
97 for (int i = 0; i < v.size(); ++i)
98 cout << v[i];
99
100After transformation with risk level set to ``-risk=reasonable`` (default):
101
102.. code-block:: c++
103
104 const int N = 5;
105 int arr[] = {1,2,3,4,5};
106 vector<int> v;
107 v.push_back(1);
108 v.push_back(2);
109 v.push_back(3);
110
111 // safe transform
112 for (auto & elem : arr)
113 cout << elem;
114
115 // reasonable transform
116 for (auto & elem : v)
117 cout << elem;
118
119 // reasonable transform
120 for (auto & elem : v)
121 cout << elem;
122
Edwin Vane3cb833d2013-03-26 16:44:29 +0000123Limitations
124===========
125
126There are certain situations where the tool may erroneously perform
127transformations that remove information and change semantics. Users of the tool
128should be aware of the behaviour and limitations of the transform outlined by
129the cases below.
130
131Comments inside loop headers
Edwin Vane8d286462013-06-14 15:14:20 +0000132----------------------------
Edwin Vane3cb833d2013-03-26 16:44:29 +0000133
134Comments inside the original loop header are ignored and deleted when
135transformed.
136
137.. code-block:: c++
138
139 for (int i = 0; i < N; /* This will be deleted */ ++i) { }
140
141Range-based loops evaluate end() only once
Edwin Vane8d286462013-06-14 15:14:20 +0000142------------------------------------------
Edwin Vane3cb833d2013-03-26 16:44:29 +0000143
144The C++11 range-based for loop calls ``.end()`` only once during the
145initialization of the loop. If in the original loop ``.end()`` is called after
146each iteration the semantics of the transformed loop may differ.
147
148.. code-block:: c++
149
150 // The following is semantically equivalent to the C++11 range-based for loop,
151 // therefore the semantics of the header will not change.
152 for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }
153
154 // Instead of calling .end() after each iteration, this loop will be
155 // transformed to call .end() only once during the initialization of the loop,
156 // which may affect semantics.
157 for (iterator it = container.begin(); it != container.end(); ++it) { }
158
159.. _IncorrectRiskyTransformation:
160
161As explained above, calling member functions of the container in the body
162of the loop is considered `risky`. If the called member function modifies the
163container the semantics of the converted loop will differ due to ``.end()``
164being called only once.
165
166.. code-block:: c++
167
168 bool flag = false;
169 for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) {
170 // Add a copy of the first element to the end of the vector.
171 if (!flag) {
172 // This line makes this transformation 'risky'.
173 vec.push_back(*it);
174 flag = true;
175 }
176 cout << *it;
177 }
178
179The original code above prints out the contents of the container including the
180newly added element while the converted loop, shown below, will only print the
181original contents and not the newly added element.
182
183.. code-block:: c++
184
185 bool flag = false;
186 for (auto & elem : vec) {
187 // Add a copy of the first element to the end of the vector.
188 if (!flag) {
189 // This line makes this transformation 'risky'
190 vec.push_back(elem);
191 flag = true;
192 }
193 cout << elem;
194 }
195
196Semantics will also be affected if ``.end()`` has side effects. For example, in
197the case where calls to ``.end()`` are logged the semantics will change in the
198transformed loop if ``.end()`` was originally called after each iteration.
199
200.. code-block:: c++
201
202 iterator end() {
203 num_of_end_calls++;
204 return container.end();
205 }
206
207Overloaded operator->() with side effects
Edwin Vane8d286462013-06-14 15:14:20 +0000208-----------------------------------------
Edwin Vane3cb833d2013-03-26 16:44:29 +0000209
210Similarly, if ``operator->()`` was overloaded to have side effects, such as
211logging, the semantics will change. If the iterator's ``operator->()`` was used
212in the original loop it will be replaced with ``<container element>.<member>``
213instead due to the implicit dereference as part of the range-based for loop.
214Therefore any side effect of the overloaded ``operator->()`` will no longer be
215performed.
216
217.. code-block:: c++
218
219 for (iterator it = c.begin(); it != c.end(); ++it) {
220 it->func(); // Using operator->()
221 }
222 // Will be transformed to:
223 for (auto & elem : c) {
224 elem.func(); // No longer using operator->()
225 }
226
227Pointers and references to containers
Edwin Vane8d286462013-06-14 15:14:20 +0000228-------------------------------------
Edwin Vane3cb833d2013-03-26 16:44:29 +0000229
230While most of the transform's risk analysis is dedicated to determining whether
231the iterator or container was modified within the loop, it is possible to
232circumvent the analysis by accessing and modifying the container through a
233pointer or reference.
234
235If the container were directly used instead of using the pointer or reference
236the following transformation would have only been applied at the ``-risk=risky``
237level since calling a member function of the container is considered `risky`.
238The transform cannot identify expressions associated with the container that are
239different than the one used in the loop header, therefore the transformation
240below ends up being performed at the ``-risk=safe`` level.
241
242.. code-block:: c++
243
244 vector<int> vec;
245
246 vector<int> *ptr = &vec;
247 vector<int> &ref = vec;
248
249 for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) {
250 if (!flag) {
251 // Accessing and modifying the container is considered risky, but the risk
252 // level is not raised here.
253 ptr->push_back(*it);
254 ref.push_back(*it);
255 flag = true;
256 }
257 }