Skip to content

Commit 5fd3786

Browse files
committed
Merge branch 'leo/113-expr_func_2022' into 'master'
Instrument expression functions using declare expressions in Ada 2022 Closes #113 See merge request eng/cov/gnatcoverage!244 Until this change, all methods used to instrument expression functions had limitations that made them unsuitable for use in all scenarios, and some expression functions had no viable instrumentation scheme at all. Ada 2022 introduces declare expressions, which can be used to declare local objects inside an expression, which was precisely what was missing in order to fully support expression functions. This change adds a new instrumentation strategy for expression functions in Ada 2022 sources. Closes eng/cov/gnatcoverage#113
2 parents 4abed9c + c69b734 commit 5fd3786

File tree

14 files changed

+382
-40
lines changed

14 files changed

+382
-40
lines changed

doc/gnatcov/src_traces.rst

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -374,51 +374,53 @@ Unsupported source constructs
374374
There are a few language constructs that |gcvins| doesn't support.
375375
The tool emits a warning when it encounters such cases and the corresponding
376376
code is not instrumented. Source coverage obligations are still emitted, and
377-
the unsupported constructs will be reported in a separate
377+
the unsupported constructs will be reported in a separate
378378
``Undetermined_Coverage`` category, to differentiate them from actual coverage
379379
violations.
380380

381381
The list of unsupported constructs is as follows:
382382

383-
* Generic expression functions,
384383
* Generic null procedures,
384+
* Protected bodies entry guards when the ``Simple_Barriers`` restriction or
385+
the ``Pure_Barriers`` one apply.
386+
387+
Additionally, if the Ada language version in use, indicated to the tool by
388+
either a ``pragma Ada_nnnn`` pragma in the sources or through the ``--ada``
389+
command line switch, is less or equal to Ada 2012, the following constructs are
390+
also unsupported:
391+
392+
* Generic expression functions,
385393
* Recursive expression functions which are primitives of some tagged type,
386394
* Expression functions which are primitives of their return type, when it is a
387395
tagged type.
388-
* Protected bodies entry guards when the ``Simple_Barriers`` restriction or
389-
the ``Pure_Barriers`` one apply.
390396

391397
The simplest way to work around the limitation concerning expression functions
392398
is to turn them into regular functions, by giving them a proper body,
393399
containing a single return statement with the original expression.
394400
Otherwise it is possible to exempt those constructs (see :ref:`exemptions`)
395401
and/or perform a manual coverage analysis for these special cases.
396402

397-
Additionally, the MC/DC instrumentation of decisions with many conditions
398-
may require more memory than available (during instrumentation and/or at
399-
run-time) to enumerate the possible paths through the decision. To avoid this,
400-
|gcv| will not instrument such decisions for MC/DC, emitting a warning in the
401-
process, and the MC/DC coverage for each decision will be reported as
402-
``Undetermined_Coverage`` state. Should the default limit not be satisfactory,
403-
it can be tuned with the option :cmd-option:`--path-count-limit`.
403+
The MC/DC instrumentation of decisions with many conditions may require more
404+
memory than available (during instrumentation and/or at run-time) to enumerate
405+
the possible paths through the decision. To avoid this, |gcv| will not
406+
instrument such decisions for MC/DC, emitting a warning in the process, and the
407+
MC/DC coverage for each decision will be reported as ``Undetermined_Coverage``
408+
state. Should the default limit not be satisfactory, it can be tuned with the
409+
option :cmd-option:`--path-count-limit`.
404410

405-
Source-coverage obligations limitations
411+
Other source-traces limitations
406412
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
407413

408414
In Ada, variable or type declarations at the package level can yield elaboration
409415
code. Such code constructs are thus considered to have corresponding coverage
410416
obligations
411417

412-
In the case where a `pragma Preelaborate` or the `No_Elaboration_Code`
413-
restriction affects the instrumented unit, variable / type declarations at the
414-
package level are not considered as coverage obligations. In the former case,
415-
elaboration code can still be emitted (in rare occurrences), but the pragma is
416-
too restrictive to instrument such code constructs. In the latter case, no
417-
elaboration code can be emitted so it is valid not to produce any coverage
418-
obligation.
419-
420-
Global source traces limitations
421-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
418+
In the case where a `pragma Preelaborate` restriction affects the instrumented
419+
unit, variable and type declarations at the package level are not considered as
420+
coverage obligations, although some elaboration code may still be emitted in
421+
rare instances. Note that declarations within a unit constrained by a
422+
``No_Elaboration_Code`` pragma don't produce coverage obligation either, which
423+
is always correct as no executable code can be emitted by the compiler for them.
422424

423425
There are also a few limitations concerning the source trace workflow as a
424426
whole:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
pragma Ada_2022;
2+
3+
package Gen is
4+
5+
generic
6+
type Value_Type is private;
7+
with function Weight (Self : Value_Type) return Natural;
8+
function Weight_Sum (Left, Right : Value_Type) return Natural;
9+
10+
generic
11+
type Value_Type is private;
12+
with function Weight (Self : Value_Type) return Natural;
13+
function Both_Weights_Null (Left, Right : Value_Type) return Boolean;
14+
15+
function Weight_Sum (Left, Right : Value_Type) return Natural is
16+
(Weight (Left) + Weight (Right)); -- # expr_st
17+
18+
function Both_Weights_Null (Left, Right : Value_Type) return Boolean is
19+
(Weight (Left) = 0 and then Weight (Right) = 0); -- # expr_dc :o/e:
20+
21+
end Gen;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
pragma Ada_2022;
2+
3+
package body Prim is
4+
5+
function Make_Internal (Cond : Boolean) return T is
6+
(T'(X => (if Cond then 1 else 2))); -- # expr_dc :o/d:
7+
8+
function Make_Internal (Cond : Boolean) return TT is
9+
(TT'(X => 3, Y => 4)); -- # expr_st
10+
11+
end Prim;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
pragma Ada_2022;
2+
3+
package Prim is
4+
type T is tagged record
5+
X : Integer;
6+
end record;
7+
function And_Then (X : T; A, B : Boolean) return Boolean is
8+
(A and then B); -- # expr_dc :o/e:
9+
function Or_Else (X : T; A, B : Boolean) return Boolean is
10+
(A or else B); -- # expr_dc :o/e:
11+
12+
function Make_Internal (Cond : Boolean) return T;
13+
14+
function Make (Cond : Boolean) return T is (Make_Internal (Cond)); -- # expr_st
15+
16+
type TT is new T with record
17+
Y : Integer;
18+
end record;
19+
20+
overriding function Make_Internal (Cond : Boolean) return TT;
21+
22+
overriding function Make (Cond : Boolean) return TT is
23+
(Make_Internal (Cond)); -- # expr_st
24+
25+
end Prim;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
pragma Ada_2022;
2+
3+
package Rec is
4+
5+
type T is tagged record
6+
X : Positive;
7+
end record;
8+
9+
function Fact (Input : T) return Positive is
10+
(if Input.X = 1 then 1 else Input.X * Fact ((X => Input.X - 1))); -- # expr_dc :o/d:
11+
12+
end Rec;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
pragma Ada_2012;
2+
3+
with Gen; use Gen;
4+
with Prim; use Prim;
5+
with Rec; use Rec;
6+
7+
with Support; use Support;
8+
9+
procedure Test_All is
10+
function Identity (X : Natural) return Natural is (X);
11+
function Nat_Sum is new Gen.Weight_Sum
12+
(Value_Type => Natural, Weight => Identity);
13+
function Nat_Both_Null is new Gen.Both_Weights_Null
14+
(Value_Type => Natural, Weight => Identity);
15+
Val : Prim.T := (X => 0);
16+
Res_T : Prim.T;
17+
Res_TT : Prim.TT;
18+
begin
19+
-- Ensure we get full coverage
20+
Assert (Nat_Sum (1, 1) = 2);
21+
Assert (Nat_Both_Null (0, 0));
22+
Assert (not Nat_Both_Null (0, 1));
23+
Assert (not Nat_Both_Null (1, 0));
24+
Assert (Fact ((X => 3)) = 6);
25+
Assert (And_Then (Val, True, True));
26+
Assert (not And_Then (Val, True, False));
27+
Assert (not And_Then (Val, False, True));
28+
Assert (not Or_Else (Val, False, False));
29+
Assert (Or_Else (Val, True, False));
30+
Assert (Or_Else (Val, False, True));
31+
Res_T := Make (True);
32+
Assert (Res_T.X = 1);
33+
Res_T := Make (False);
34+
Assert (Res_T.X = 2);
35+
Res_TT := Make (False);
36+
Assert (Res_TT.X = 3 and then Res_TT.Y = 4);
37+
end Test_All;
38+
39+
--# rec.ads prim.ads prim.adb gen.ads
40+
--
41+
-- /expr/ l+ ## 0
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
pragma Ada_2012;
2+
3+
with Gen; use Gen;
4+
with Prim; use Prim;
5+
with Rec; use Rec;
6+
7+
with Support; use Support;
8+
9+
procedure Test_True is
10+
function Identity (X : Natural) return Natural is (X);
11+
function Nat_Sum is new Gen.Weight_Sum
12+
(Value_Type => Natural, Weight => Identity);
13+
function Nat_Both_Null is new Gen.Both_Weights_Null
14+
(Value_Type => Natural, Weight => Identity);
15+
Val : Prim.T := (X => 0);
16+
Res_T : Prim.T;
17+
Res_TT : Prim.TT;
18+
begin
19+
Assert (Nat_Sum (1, 1) = 2);
20+
Assert (Nat_Both_Null (0, 0));
21+
Assert (Fact ((X => 1)) = 1);
22+
Assert (Or_Else (Val, False, True));
23+
Assert (And_Then (Val, True, True));
24+
Res_T := Make (True);
25+
Assert (Res_T.X = 1);
26+
Res_TT := Make (False);
27+
Assert (Res_TT.X = 3 and then Res_TT.Y = 4);
28+
end Test_True;
29+
30+
--# rec.ads prim.ads prim.adb gen.ads
31+
--
32+
-- /expr_st/ l+ ## 0
33+
-- /expr_dc/ l! ## oF-
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
7.1.2 DEAD Ada 2022 not supported on old toolchains
2+
5.04a1 DEAD Ada 2022 not supported on old toolchains
3+
bin-traces DEAD Ada 2022 not supported on bin traces
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""
2+
Check that limitations concerning expression function instrumentation are lifted
3+
when Ada 2022, and thus declare expressions are available.
4+
5+
This test reflects the various limitations that are documented in the user
6+
manual:
7+
- generic expression functions (gen.ads)
8+
- expression functions which are primitives of a tagged type, when that type is
9+
the return type of the expression function. (prim.ads)
10+
- Recursive expression functions which are a primitive of some type (rec.ads)
11+
12+
The above constructs used to not be instrumented by gnatcov and resulted in
13+
warnings and undetermined coverage items in the reports.
14+
"""
15+
16+
from SCOV.tc import TestCase
17+
from SCOV.tctl import CAT
18+
from SUITE.context import thistest
19+
20+
21+
TestCase(category=CAT.mcdc).run()
22+
thistest.result()

tools/gnatcov/gnatcov_bits_specific.adb

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,26 +1319,14 @@ begin
13191319
Has_Matcher : Boolean;
13201320
-- Matcher for the source files to ignore
13211321

1322-
Language_Version : Any_Language_Version;
1323-
pragma Unreferenced (Language_Version);
1324-
13251322
begin
13261323
Create_Matcher (Ignored_Source_Files, Matcher, Has_Matcher);
13271324

13281325
declare
13291326
V : constant String := Value (Args, Opt_Ada, "2012");
13301327
begin
1331-
if V in "83" | "1983" then
1332-
Language_Version := Ada_83;
1333-
elsif V in "95" | "1995" then
1334-
Language_Version := Ada_95;
1335-
elsif V in "05" | "2005" then
1336-
Language_Version := Ada_2005;
1337-
elsif V in "12" | "2012" then
1338-
Language_Version := Ada_2012;
1339-
elsif V in "22" | "2022" then
1340-
Language_Version := Ada_2022;
1341-
else
1328+
if not Set_Language_Version (Global_Language_Version, From => V)
1329+
then
13421330
Fatal_Error ("Bad Ada language version: " & V);
13431331
end if;
13441332
end;

0 commit comments

Comments
 (0)