Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Ada not trapping this specified range check?

Tags:

ada

I am going through the learn.adacore.com tutorial and have hit upon a problem that I am unsure of.

Specifically I understand that Ada is designed to trap attempts to overflow a variable with a specified range definition.

In the case below, the first attempt to do this causes a compiler 'range check failure' which is expected. However the following line doesn't trap it and I am not sure why:

with Ada.Text_IO; use Ada.Text_IO;

procedure Custom_Floating_Types is
    type T_Norm is new float range -1.0 .. 1.0;

    D : T_Norm := 1.0;
begin
    Put_Line("The value of D =" & T_Norm'Image(D));
    -- D := D + 1.0; -- This causes a range check failure at run time = completely expected.
    Put_Line("The value of D =" & T_Norm'Image(D + 1.0)); -- This doesn't?

end Custom_Floating_Types;
like image 552
hypotenuse Avatar asked Feb 14 '21 22:02

hypotenuse


4 Answers

You have a couple of pretty good answers, but I'm going to add another because it's clear that you expect the expression D + 1.0 to raise an exception, and the answers you have don't explain why it doesn't.

A type declaration like

type T_Norm is new float range -1.0 .. 1.0;

is roughly equivalent to

type T_Norm'Base is new Float;
subtype T_Norm is T_Norm'Base range -1.0 .. 1.0;

The type (called the "base type") isn't given a name, though it can often be referenced with the 'Base attribute. The name that is given is to a subtype, called the "first-named subtype".

This distinction is important and is often not given enough attention. As explained by egilhh, T_Norm'Image is defined in terms of T_Norm'Base. This is also true of the arithmetic operators. For example, "+" is defined as

function "+" (Left : in T_Norm'Base; Right : in T_Norm'Base) return T_Norm'Base;

2.0 is clearly in the range of T_Norm'Base, so evaluating D + 1.0 doesn't violate any constraints, nor does passing it to T_Norm'Image. However, when you try to assign the resulting value to D, which has subtype T_Norm, a check is performed that the value is in the range of the subtype, and an exception is raised because the check fails.

This distinction is used in other places to make the language work reasonably. For example, a constrained array type

type A is array (1 .. 10) of C;

is roughly equivalent to

type A'Base is array (Integer range <>) of C;
subtype A is A'Base (1 .. 10);

If you do

V : A;
... V (2 .. 4)

you might expect problems because the slice doesn't have the bounds of A. But it works because the slice doesn't have subtype A but rather the anonymous subtype A'Base (2 ..4).

like image 157
Jeffrey R. Carter Avatar answered Oct 14 '22 19:10

Jeffrey R. Carter


The definition of 'Image says:

For every scalar subtype S:

S'Image denotes a function with the following specification:

function S'Image(Arg : S'Base) return String

As you can see, it takes a parameter of the base (unconstrained) type

like image 28
egilhh Avatar answered Oct 14 '22 18:10

egilhh


T_Norm'Image(D + 1.0) neither assigns nor reads an out-of-range value. It asks for the 'Image attribute (string representation) of (D + 1.0), which is the same as asking for (1.0 + 1.0).

I can see two ways the confusion might arise. First, the name "attribute" could be misinterpreted to suggest that 'Image involves something intrinsic to D. It doesn't. The 'Image attribute is just a function, so D is just part of the expression that defines the value of the parameter (in your example = 2.0).

Second, the 'Image attribute comes from Float. Thus, any Float can be its parameter.

You can create a function that accepts only parameters of T_Norm'Range and make that function an attribute of T_Norm. See Ada Reference Manual 4.10.

like image 43
ds_j2 Avatar answered Oct 14 '22 19:10

ds_j2


Because you don't store a value greater than range in D.

like image 41
Alborz Avatar answered Oct 14 '22 19:10

Alborz