@@ -40,7 +40,6 @@ with Coverage; use Coverage;
4040with Coverage_Options ;
4141with Hex_Images ; use Hex_Images;
4242with Inputs ; use Inputs;
43- with Instrument.C_Utils ; use Instrument.C_Utils;
4443with Outputs ; use Outputs;
4544with Paths ; use Paths;
4645with SCOs ;
@@ -221,6 +220,11 @@ package body Instrument.C is
221220 Loc : Source_Location_T;
222221 Text : String);
223222
223+ overriding procedure Register_CXX_For_Range
224+ (Pass : Instrument_Pass_Kind;
225+ UIC : in out C_Unit_Inst_Context'Class;
226+ N : Cursor_T);
227+
224228 Record_PP_Info_Pass : aliased Record_PP_Info_Pass_Kind;
225229 Instrument_Pass : aliased Instrument_Pass_Kind;
226230
@@ -281,6 +285,54 @@ package body Instrument.C is
281285 function Insert_MCDC_State
282286 (UIC : in out C_Unit_Inst_Context'Class; Name : String) return String;
283287
288+ procedure Fix_CXX_For_Ranges (UIC : in out C_Unit_Inst_Context);
289+ -- This procedure fixes C++ for ranges that were purposefully instrumented
290+ -- wrongly. To instrument a C++ for range, we actually need to turn
291+ --
292+ -- for (int i = 0; auto j : {1, 2}) {}
293+ --
294+ -- into
295+ --
296+ -- {
297+ -- witness_i(); int i = 0;
298+ -- witness_j();
299+ -- for (auto j : {1, 2}) {}
300+ -- }
301+ --
302+ -- We introduce an outer scope to leave the initializer declaration
303+ -- lifetime unchanged, and to be able to easily instrument both the
304+ -- initializer declaration and the range expression.
305+ --
306+ -- Note that this is valid because you can't goto inside the loop
307+ -- (and thus skip the execution of witness_j) as it is
308+ -- forbidden to bypass declarations with initialization in C++,
309+ -- which "auto j : {1, 2}" is.
310+ --
311+ -- Though we can't do that all at once, as this changes the shape as the
312+ -- AST. As we rely on the AST node location to instrument statements and
313+ -- decisions, we would be instrumenting the decision at the wrong place,
314+ -- as we would instrument the initialization statement by moving it.
315+ --
316+ -- Thus, we do the normal instrumentation process, which will yield an
317+ -- unvalid C++ sequence, that we can easily fix in this procedure, by
318+ -- moving around the rewritten code.
319+ --
320+ -- The for ranges at the entry of this procedure will have been
321+ -- instrumented as followed (the added code is identified by <>):
322+ --
323+ -- <witness_j();> for (<witness_i();> int i = 0; auto j : {1, 2}) {}<}>
324+ --
325+ -- Two things to note here: the witness_j is executed before "int i = 0;"
326+ -- (which is wrong), and there is an unmatched closing brace.
327+ --
328+ -- To fix this, we actually need to move both the added code, _and_ the
329+ -- initializer statement before the <witness_j();>, and insert an opening
330+ -- brace before:
331+ --
332+ -- <{><witness_i();> int i = 0; <witness_j();> for (auto j : {1, 2}) {}<}>
333+ --
334+ -- and now we have valid, and correctly instrumented code.
335+
284336 -- --------------------------
285337 -- Source level rewriting --
286338 -- --------------------------
@@ -1030,6 +1082,18 @@ package body Instrument.C is
10301082 end if ;
10311083 end Insert_Text_After ;
10321084
1085+ -- --------------------------
1086+ -- Register_CXX_For_Range --
1087+ -- --------------------------
1088+
1089+ overriding procedure Register_CXX_For_Range
1090+ (Pass : Instrument_Pass_Kind;
1091+ UIC : in out C_Unit_Inst_Context'Class;
1092+ N : Cursor_T) is
1093+ begin
1094+ UIC.Instrumented_CXX_For_Ranges.Append (N);
1095+ end Register_CXX_For_Range ;
1096+
10331097 -- ------------------------
10341098 -- Instrument_Statement --
10351099 -- ------------------------
@@ -1306,6 +1370,43 @@ package body Instrument.C is
13061370 return Name;
13071371 end Insert_MCDC_State ;
13081372
1373+ -- ----------------------
1374+ -- Fix_CXX_For_Ranges --
1375+ -- ----------------------
1376+
1377+ procedure Fix_CXX_For_Ranges (UIC : in out C_Unit_Inst_Context) is
1378+ begin
1379+ for N of UIC.Instrumented_CXX_For_Ranges loop
1380+ declare
1381+ Loc : constant Source_Location_T := Start_Sloc (N);
1382+ For_Init_Stmt : constant Cursor_T := Get_For_Init (N);
1383+ For_Init_Rng : constant Source_Range_T :=
1384+ Get_Cursor_Extent (For_Init_Stmt);
1385+ begin
1386+ -- Get the rewritten text for the initializing statement and
1387+ -- move it before any rewritten text before the for statement.
1388+
1389+ CX_Rewriter_Insert_Text_Before
1390+ (UIC.Rewriter,
1391+ Loc,
1392+ CX_Rewriter_Get_Rewritten_Text (UIC.Rewriter, For_Init_Rng));
1393+
1394+ -- Open the outer scope. It was closed during the instrumentation
1395+ -- process, so we do not need to take care of that.
1396+
1397+ CX_Rewriter_Insert_Text_Before (UIC.Rewriter, Loc, " {" );
1398+ CX_Rewriter_Remove_Text (UIC.Rewriter, For_Init_Rng);
1399+
1400+ -- for (; auto i : {1, 2}) is invalid C++ code so insert a dummy
1401+ -- initializing expression to avoid the null statement here, as
1402+ -- it is easier than trying to move around the comma.
1403+
1404+ CX_Rewriter_Insert_Text_Before
1405+ (UIC.Rewriter, Start_Sloc (For_Init_Stmt), " true" );
1406+ end ;
1407+ end loop ;
1408+ end Fix_CXX_For_Ranges ;
1409+
13091410 type SC_Entry is record
13101411 N : Cursor_T;
13111412 -- Original statement node, used to compute macro expansion information
@@ -2134,14 +2235,28 @@ package body Instrument.C is
21342235 -- the range declaration initialization expression. Like all
21352236 -- statements, they can contain nested decisions.
21362237
2137- Extend_Statement_Sequence
2138- (For_Init_Stmt, ' ' , Insertion_N => N);
2139- Process_Decisions_Defer (For_Init_Stmt, ' X' );
2238+ if not Is_Null (For_Init_Stmt) then
2239+
2240+ -- See Fix_CXX_For_Ranges for an explanation of the
2241+ -- below code.
2242+
2243+ Extend_Statement_Sequence
2244+ (For_Init_Stmt, ' ' , Insertion_N => For_Init_Stmt);
2245+ Process_Decisions_Defer (For_Init_Stmt, ' X' );
2246+
2247+ -- Preemptively end the introduced outer scope as it is
2248+ -- easier done when traversing the AST.
2249+
2250+ Append (Trailing_Braces, ' }' );
2251+ UIC.Pass.Register_CXX_For_Range (UIC, N);
2252+ end if ;
2253+
2254+ -- Instrument the range as mentioned above
21402255
21412256 Extend_Statement_Sequence
21422257 (For_Range_Decl, ' ' ,
2143- Insertion_N => For_Range_Decl ,
2144- Instr_Scheme => Instr_Expr );
2258+ Insertion_N => N ,
2259+ Instr_Scheme => Instr_Stmt );
21452260 Process_Decisions_Defer (For_Range_Decl, ' X' );
21462261
21472262 Set_Statement_Entry;
@@ -3315,6 +3430,11 @@ package body Instrument.C is
33153430 end loop ;
33163431 end ;
33173432
3433+ -- Once everything has been instrumented, fixup the for ranges. See the
3434+ -- documentation of Fix_CXX_For_Ranges.
3435+
3436+ Fix_CXX_For_Ranges (UIC);
3437+
33183438 -- We import the extern declaration of symbols instead of including the
33193439 -- header where they are defined.
33203440 --
0 commit comments