Skip to content

Commit 0081d55

Browse files
authored
Merge pull request #1123 from gusthoff/content/advanced_ada/new_content/records/discriminant_constraints_operations/20241005
Adding section on discriminant constraints and operations
2 parents ea67f46 + f90f1fe commit 0081d55

File tree

2 files changed

+301
-171
lines changed

2 files changed

+301
-171
lines changed

content/courses/advanced-ada/parts/data_types/records.rst

Lines changed: 300 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,11 +1071,15 @@ component :ada:`Arr`. Note that the same discriminant part must appear in both
10711071
:ref:`the partial and the full view <Adv_Ada_Type_View>` of type :ada:`T`.
10721072

10731073

1074+
.. _Adv_Ada_Record_Discriminants_Object_Declaration:
1075+
10741076
Object declaration
10751077
~~~~~~~~~~~~~~~~~~
10761078

10771079
As we've already seen, we declare objects of a type :ada:`T` with a
10781080
discriminant :ada:`D` by specifying the actual value of discriminant :ada:`D`.
1081+
This is called a
1082+
:ref:`discriminant constraint <Adv_Ada_Record_Discriminant_Constraints>`.
10791083
For example:
10801084

10811085
.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants.Objects_Discriminants
@@ -2108,20 +2112,307 @@ value set for the parent type :ada:`T`.
21082112
Instead of using :ada:`<>`, we have to repeat the value explicitly.
21092113

21102114

2111-
..
2112-
TO BE DONE:
2115+
.. _Adv_Ada_Record_Discriminant_Constraints_Operations:
21132116

2114-
Discriminant constraints and operations
2115-
---------------------------------------
2117+
Discriminant constraints and operations
2118+
---------------------------------------
21162119

2117-
.. admonition:: In the Ada Reference Manual
2120+
In this section, we discuss some details about discriminant constraints and
2121+
operations related to discriminants |mdash| more specifically, the
2122+
:ada:`Constrained` attribute.
21182123

2119-
- :arm:`3.7.1 Discriminant Constraints <3-7-1>`
2124+
.. admonition:: In the Ada Reference Manual
21202125

2121-
.. todo::
2126+
- :arm:`3.7.1 Discriminant Constraints <3-7-1>`
2127+
2128+
2129+
.. _Adv_Ada_Record_Discriminant_Constraints:
2130+
2131+
Discriminant constraints
2132+
~~~~~~~~~~~~~~~~~~~~~~~~
2133+
2134+
As we discussed before, when
2135+
:ref:`declaring an object with a discriminant <Adv_Ada_Record_Discriminants_Object_Declaration>`,
2136+
we have to specify the values of the all discriminants |mdash| unless, of
2137+
course, those discriminants have a
2138+
:ref:`default value <Adv_Ada_Record_Discriminants_Default_Values>`. The values
2139+
we specify for the discriminants are called discriminant constraints.
2140+
2141+
Let's revisit the code example we've seen earlier on:
2142+
2143+
.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Discriminant_Constraint
2144+
2145+
package Recs is
2146+
2147+
type T (L : Positive;
2148+
M : Positive) is
2149+
null record;
2150+
2151+
end Recs;
2152+
2153+
with Recs; use Recs;
2154+
2155+
procedure Show_Object_Declaration is
2156+
A : T (L => 5, M => 6);
2157+
B : T (7, 8);
2158+
C : T (7, M => 8);
2159+
begin
2160+
null;
2161+
end Show_Object_Declaration;
2162+
2163+
Here, :ada:`L => 5, M => 6` (for object :ada:`A`) are named constraints, while
2164+
:ada:`7, 8` (for object :ada:`B`) are positional constraints.
2165+
2166+
It's possible to use both positional and named constraints, as we do for object
2167+
:ada:`C`: :ada:`7, M => 8`. In this case, the positional associations must
2168+
precede the named associations.
2169+
2170+
In the case of named constraints, we can use multiple selector names:
2171+
2172+
.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Discriminant_Constraint
2173+
2174+
with Recs; use Recs;
2175+
2176+
procedure Show_Object_Declaration is
2177+
A : T (L | M => 5);
2178+
-- ^^^^^
2179+
-- multiple selector names
2180+
begin
2181+
null;
2182+
end Show_Object_Declaration;
2183+
2184+
This is only possible, however, if those named discriminants are all of the
2185+
same type. (In this case, :ada:`L` and :ada:`M` are both of :ada:`Positive`
2186+
subtype.)
2187+
2188+
.. admonition:: In the Ada Reference Manual
2189+
2190+
- :arm22:`3.7.1 Discriminant Constraints <3-7-1>`
2191+
2192+
2193+
Discriminant constraint in subtypes
2194+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2195+
2196+
We can use discriminant constraints in the declaration of subtypes. For
2197+
example:
2198+
2199+
.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Discriminant_Constraint
2200+
2201+
with Recs; use Recs;
2202+
2203+
procedure Show_Object_Declaration is
2204+
subtype T_5_6 is T (L => 5, M => 6);
2205+
-- ^^^^^^^^^^^^^^
2206+
-- discriminant constraints for subtype
2207+
2208+
A : T_5_6;
2209+
begin
2210+
null;
2211+
end Show_Object_Declaration;
2212+
2213+
In this example, we use the named discriminant constraints
2214+
:ada:`L => 5, M => 6` in the declaration of the subtype :ada:`T_5_6`.
2215+
2216+
2217+
.. _Adv_Ada_Constrained_Attribute:
2218+
2219+
Constrained Attribute
2220+
~~~~~~~~~~~~~~~~~~~~~
2221+
2222+
We can use the :ada:`Constrained` attribute to verify whether an object of
2223+
discriminated type is constrained or not. Let's look at a simple example:
2224+
2225+
.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Simple_Constrained_Attribute
2226+
2227+
package Recs is
2228+
2229+
type T (L : Positive := 1) is
2230+
null record;
2231+
2232+
end Recs;
2233+
2234+
with Ada.Text_IO; use Ada.Text_IO;
2235+
2236+
with Recs; use Recs;
2237+
2238+
procedure Show_Constrained_Attribute is
2239+
Constr : T (L => 5);
2240+
-- ^^^^^^ constrained.
2241+
Unconstr : T;
2242+
-- ^ unconstrained;
2243+
-- using defaults.
2244+
begin
2245+
Put_Line ("Constr'Constrained: "
2246+
& Constr'Constrained'Image);
2247+
Put_Line ("Unconstr'Constrained: "
2248+
& Unconstr'Constrained'Image);
2249+
end Show_Constrained_Attribute;
2250+
2251+
As the :ada:`Constrained` attribute indicates, the :ada:`Constr` object is
2252+
constrained (by the :ada:`L => 5` discriminant constraint), while the
2253+
:ada:`Unconstr` object is unconstrained. Note that, even though :ada:`Unconstr`
2254+
is using the default value for :ada:`L` |mdash| which would correspond to the
2255+
discriminant constraint :ada:`L => 1` |mdash| the object itself hasn't been
2256+
constraint at its declaration.
2257+
2258+
Let's continue our discussion with a more complex example by reusing
2259+
the :ada:`Unconstrained_Types` package that we declared in a
2260+
:ref:`previous section <Adv_Ada_Definite_Indefinite_Subtypes>`. In this
2261+
version of the package, we're adding a :ada:`Reset` procedure for the
2262+
discriminated record type :ada:`Simple_Record`:
2263+
2264+
.. code:: ada compile_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Constrained_Attribute
2265+
2266+
package Unconstrained_Types is
2267+
2268+
type Simple_Record
2269+
(Extended : Boolean := False) is
2270+
record
2271+
V : Integer;
2272+
case Extended is
2273+
when False =>
2274+
null;
2275+
when True =>
2276+
V_Float : Float;
2277+
end case;
2278+
end record;
2279+
2280+
procedure Reset (R : in out Simple_Record);
2281+
2282+
end Unconstrained_Types;
2283+
2284+
with Ada.Text_IO; use Ada.Text_IO;
2285+
2286+
package body Unconstrained_Types is
2287+
2288+
procedure Reset (R : in out Simple_Record) is
2289+
Zero_Not_Extended : constant
2290+
Simple_Record := (Extended => False,
2291+
V => 0);
2292+
2293+
Zero_Extended : constant
2294+
Simple_Record := (Extended => True,
2295+
V => 0,
2296+
V_Float => 0.0);
2297+
begin
2298+
Put_Line ("---- Reset: R'Constrained => "
2299+
& R'Constrained'Image);
2300+
2301+
if not R'Constrained then
2302+
R := Zero_Extended;
2303+
else
2304+
if R.Extended then
2305+
R := Zero_Extended;
2306+
else
2307+
R := Zero_Not_Extended;
2308+
end if;
2309+
end if;
2310+
end Reset;
2311+
2312+
end Unconstrained_Types;
2313+
2314+
As the name indicates, the :ada:`Reset` procedure initializes all record
2315+
components with zero. Note that we use the :ada:`Constrained` attribute to
2316+
verify whether objects are constrained before assigning to them. For objects
2317+
that are not constrained, we can simply assign another object to it |mdash| as
2318+
we do with the :ada:`R := Zero_Extended` statement. When an object is
2319+
constrained, however, the discriminants must match. If we assign an object to
2320+
:ada:`R`, the discriminant of that object must match the discriminant of
2321+
:ada:`R`. This is the kind of verification that we do in the :ada:`else` part
2322+
of that procedure: we check the state of the :ada:`Extended` discriminant
2323+
before assigning an object to the :ada:`R` parameter.
2324+
2325+
The :ada:`Using_Constrained_Attribute` procedure below declares two objects of
2326+
:ada:`Simple_Record` type: :ada:`R1` and :ada:`R2`. Because the
2327+
:ada:`Simple_Record` type has a default value for its discriminant, we can
2328+
declare objects of this type without specifying a value for the discriminant.
2329+
This is exactly what we do in the declaration of :ada:`R1`. Here, we don't
2330+
specify any constraints, so that it takes the default value
2331+
(:ada:`Extended => False`). In the declaration of :ada:`R2`, however, we
2332+
explicitly set :ada:`Extended` to :ada:`False`:
2333+
2334+
.. code:: ada run_button project=Courses.Advanced_Ada.Data_Types.Records.Discriminants_Constraints_Operations.Constrained_Attribute
2335+
2336+
with Ada.Text_IO; use Ada.Text_IO;
2337+
2338+
with Unconstrained_Types; use Unconstrained_Types;
2339+
2340+
procedure Using_Constrained_Attribute is
2341+
R1 : Simple_Record;
2342+
R2 : Simple_Record (Extended => False);
2343+
2344+
procedure Show_Rs is
2345+
begin
2346+
Put_Line ("R1'Constrained => "
2347+
& R1'Constrained'Image);
2348+
Put_Line ("R1.Extended => "
2349+
& R1.Extended'Image);
2350+
Put_Line ("--");
2351+
Put_Line ("R2'Constrained => "
2352+
& R2'Constrained'Image);
2353+
Put_Line ("R2.Extended => "
2354+
& R2.Extended'Image);
2355+
Put_Line ("----------------");
2356+
end Show_Rs;
2357+
begin
2358+
Show_Rs;
2359+
2360+
Reset (R1);
2361+
Reset (R2);
2362+
Put_Line ("----------------");
2363+
2364+
Show_Rs;
2365+
end Using_Constrained_Attribute;
2366+
2367+
When we run this code, the user messages from :ada:`Show_Rs` indicate to us
2368+
that :ada:`R1` is not constrained, while :ada:`R2` is constrained.
2369+
Because we declare :ada:`R1` without specifying a value for the :ada:`Extended`
2370+
discriminant, :ada:`R1` is not constrained. In the declaration of
2371+
:ada:`R2`, on the other hand, the explicit value for the :ada:`Extended`
2372+
discriminant makes this object constrained. Note that, for both :ada:`R1` and
2373+
:ada:`R2`, the value of :ada:`Extended` is :ada:`False` in the declarations.
2374+
2375+
As we were just discussing, the :ada:`Reset` procedure includes checks to avoid
2376+
mismatches in discriminants. When we don't have those checks, we might get
2377+
exceptions at runtime. We can force this situation by replacing the
2378+
implementation of the :ada:`Reset` procedure with the following lines:
2379+
2380+
.. code-block:: ada
2381+
2382+
-- [...]
2383+
begin
2384+
Put_Line ("---- Reset: R'Constrained => "
2385+
& R'Constrained'Image);
2386+
R := Zero_Extended;
2387+
end Reset;
2388+
2389+
Running the code now generates a runtime exception:
2390+
2391+
::
2392+
2393+
raised CONSTRAINT_ERROR : unconstrained_types.adb:12 discriminant check failed
2394+
2395+
This exception is raised during the call to :ada:`Reset (R2)`. As we see in the
2396+
code, :ada:`R2` is constrained. Also, its :ada:`Extended` discriminant is set
2397+
to :ada:`False`, which means that it doesn't have the :ada:`V_Float`
2398+
component. Therefore, :ada:`R2` is not compatible with the constant
2399+
:ada:`Zero_Extended` object, so we cannot assign :ada:`Zero_Extended` to
2400+
:ada:`R2`. Also, because :ada:`R2` is constrained, its :ada:`Extended`
2401+
discriminant cannot be modified.
2402+
2403+
The behavior is different for the call to :ada:`Reset (R1)`, which works fine.
2404+
Here, when we pass :ada:`R1` as an argument to the :ada:`Reset` procedure, its
2405+
:ada:`Extended` discriminant is :ada:`False` by default. Thus, :ada:`R1` is
2406+
also not compatible with the :ada:`Zero_Extended` object. However, because
2407+
:ada:`R1` is not constrained, the assignment modifies :ada:`R1` (by changing
2408+
the value of the :ada:`Extended` discriminant). Therefore, with the call to
2409+
:ada:`Reset`, the :ada:`Extended` discriminant of :ada:`R1` changes from
2410+
:ada:`False` to :ada:`True`.
2411+
2412+
.. admonition:: In the Ada Reference Manual
2413+
2414+
- :arm22:`3.7.2 Operations of Discriminated Types <3-7-2>`
21222415

2123-
- Discriminant constraint
2124-
- (MOVE: Constrained Attribute)
21252416

21262417

21272418
..

0 commit comments

Comments
 (0)