!standard 4.3.1(17) 01-11-02 AC95-00017/01
!class amendment 01-11-02
!status received no action 01-11-02
!subject Are discriminant constraints required to be static?
!summary
!appendix
From: Dan Eilers
Sent: Friday, November 2, 2001 4:23 PM
!topic are discriminant constraints required to be static
!reference RM95-3.7.1
!from Dan Eilers 01-11-02
!discussion
procedure test1 is
type enum is (e1,e2,e3,e4,e5);
subtype subset is enum range e1..e4;
type rec(x: enum) is record
case x is
when subset => i,j,k: integer;
when others => null;
end case;
end record;
type rec_subset(y: subset := e1) is new rec(x => y);
function create(z: subset) return rec_subset is
r: rec_subset := (z, 0, 0, 0); -- legal??
begin
return r;
end;
a: rec := rec(create(e2));
begin
null;
end test1;
Does the discriminant constraint z (above) have to be static,
or is it sufficient that z's subtype statically matches (in the
sense of 4.9.1) the discriminant subtype for y on type rec_subset?
If it must be static, is there a better way to define create than
the following?
function create(z: subset) return rec_subset is
r: rec_subset;
begin
case z is
when e1 => r := (e1, 0, 0, 0);
when e2 => r := (e2, 0, 0, 0);
when e3 => r := (e3, 0, 0, 0);
when e4 => r := (e4, 0, 0, 0);
end case;
return r;
end;
What I'd really like to do is use a subtype instead of the derived
type rec_constraint (below), eliminating the need for a type conversion
in the declaration of a. Why isn't this allowed?
procedure test2 is
type enum is (e1,e2,e3,e4,e5);
subtype subset is enum range e1..e4;
type rec(x: enum) is record
case x is
when subset => i,j,k: integer;
when others => null;
end case;
end record;
subtype rec_subset(y: subset := e1) is rec(x => y); -- why not allow?
function create_subset(z: subset) return rec_subset is
r: rec_subset := (z, 0, 0, 0);
begin
return r;
end;
a: rec := create_subset(e2); -- no type conversion desired
begin
null;
end test2;
****************************************************************
From: Jeffery Carter
Sent: Friday, November 2, 2001 5:01 PM
The discriminant in the aggregate must be static, but you can always do:
R : Rec (X => Z);
...
R.I :=0;
R.J := 0;
R.K := 0;
return R;
****************************************************************
From: Tucker Taft
Sent: Friday, November 2, 2001 5:41 PM
4.3.1(17) is quite clear that in an aggregate, the discriminant
that governs a variant part must be given by a static
expression. You are right that the rule could have allowed
a non-static value so long as its value was known statically
to be within a range that determined which variant alternative
was chosen. Of course such a rule would be harder to state
and harder to implement, so I presume the perceived value
associated with the more liberal rule was not sufficient to
offset this added complexity.
[A related annoyance is that you must have an others
alternative in a nested variant part even if all possible
choices are already covered, since the relevant range
is the full range of the discriminant, rather than the
subset of the range associated with the enclosing
variant alternative(s).]
****************************************************************
From: Christoph Grein
Sent: Monday, November 5, 2001 12:48 AM
By changing your example, you can accoplish this:
> procedure test1 is
> type enum is (e1,e2,e3,e4,e5);
> subtype subset is enum range e1..e4;
> type rec (x: enum) is record
> case x is
> when subset => i,j,k: integer := 0; <----- defaults
> when others => null;
> end case;
> end record;
> type rec_subset(y: subset := e1) is new rec(x => y);
> function create(z: subset) return rec_subset is
> r: rec_subset (z); <----- need not be static
> begin
> return r;
> end;
> a: rec := rec(create(e2));
> begin
> null;
> end test1;
It's sometimes annoying that discriminants in aggregates have to be static, but
I think this is for good reasons. With the above trick, you can circumvent the
case statement.
Note that the variable r is never assigned a value, yet it is fully defined.
This conflicts with some coding standards which require that all variables must
be initialized or assigned a value.
****************************************************************
From: Dan Eilers
Sent: Wednesday, November 7, 2001 1:10 PM
Thanks! That trick solved the problem I was having.
But I disagree that there are good reasons for restricting the discriminant
to be static.
Tuck wrote:
> 4.3.1(17) is quite clear that in an aggregate, the discriminant
> that governs a variant part must be given by a static
> expression. You are right that the rule could have allowed
> a non-static value so long as its value was known statically
> to be within a range that determined which variant alternative
> was chosen. Of course such a rule would be harder to state
> and harder to implement, so I presume the perceived value
> associated with the more liberal rule was not sufficient to
> offset this added complexity.
Is it really that hard to state and/or implement?
It doesn't appear that this issue was thought through very
well. The AARM, for example, makes no mention that a more
liberal rule would have worked. In accordance with the
principle of least surprise, and to avoid the need to teach
such non-obvious tricks, I propose liberalizing the rule
to allow static scalar subtypes instead of just static values.
4.3.1(17) would be changed from:
"If the components of a variant_part are needed, then the value of
a discriminant that governs the variant_part shall be given by a
static expression."
to something like:
"If the components of a variant_part are needed, then either
o the value of a discriminant that governs the variant_part
shall be given by a static expression; or
o the discriminant shall be of a static constrained scalar subtype,
whose values are all covered by the same variant.
with a cross reference to the definition of "covered" in 3.8.1(9).
The logic to determine whether a static scalar subtype overlaps more
than one variant is already in compilers in order to enforce the
rule that variant parts don't overlap each other, so there seems
to be very little implementation burden.
p.s.
I didn't get any comments on the second part of my question.
Is there a good reason why subtype declarations aren't like
type declarations in allowing a discriminant part?
> What I'd really like to do is use a subtype instead of the derived
> type rec_constraint (below), eliminating the need for a type conversion
> in the declaration of a. Why isn't this allowed?
>
> procedure test2 is
> type enum is (e1,e2,e3,e4,e5);
> subtype subset is enum range e1..e4;
> type rec(x: enum) is record
> case x is
> when subset => i,j,k: integer;
> when others => null;
> end case;
> end record;
> subtype rec_subset(y: subset := e1) is rec(x => y); -- why not allow?
> function create_subset(z: subset) return rec_subset is
> r: rec_subset := (z, 0, 0, 0);
> begin
> return r;
> end;
> a: rec := create_subset(e2); -- no type conversion desired
> begin
> null;
> end test2;
****************************************************************
From: Robert Dewar
Sent: Wednesday, November 7, 2001 1:25 PM
<< I didn't get any comments on the second part of my question.
Is there a good reason why subtype declarations aren't like
type declarations in allowing a discriminant part?>>
This question is asked the wrong way round, there has to be a good
reason for adding a feature, not lack of a good reason for keeping it out!
To me this would be added complexity that is definitely not worth while.
****************************************************************
From: Dan Eilers
Sent: Wednesday, November 7, 2001 1:48 PM
Well, I did provide an example where a subtype was desired, and
having to use a type as a workaround results in cluttering the
code with unwanted type conversions.
RM95 Introduction, para 39, gives the purpose of subtypes:
39 The concept of a type is further refined by the concept of a subtype,
whereby a user can constrain the set of allowed values of a type. Subtypes
can be used to define subranges of scalar types, arrays with a limited set of
index values, and records and private types with particular discriminant
values.
I want to do just that, use a subtype to constrain the discriminants of
a record type, but instead of constraining them to a single value, I want
to contrain them to a range of values, just as a type declaration can do.
Orthogonality of language design would argue for subtype declarations
to behave like type declarations in their ability to constrain
discriminants to a range of values, unless there is some reason
why it causes difficulties for implementations and/or users.
****************************************************************
From: Robert Dewar
Sent: Friday, November 2, 2001 5:41 PM
I understand the request, but I think it introduces excessive complexity,
with insufficient justification of real need.
****************************************************************
From: Christoph Grein
Sent: Wednesday, November 7, 2001 11:38 AM
> > type enum is (e1,e2,e3,e4,e5);
> > subtype subset is enum range e1..e4;
> > type rec(x: enum) is record
> > case x is
> > when subset => i,j,k: integer;
> > when others => null;
> > end case;
> > end record;
> > subtype rec_subset(y: subset := e1) is rec(x => y); -- why not allow?
A possible complication are named aggregate associations.
What is the name of the component?
Ag_S: rec_subset := (y => e1, i => ...);
Ag_R: rec (e1) := (x => e1, i => ...);
This is a bit confusing, although with the context of the subtype given, might
seem OK. But what about aggregate parameters of subprograms:
procedure P (X: Rec);
P ((y => e1, i => ...)); -- subtype aggregate, legal?
P ((x => e1, i => ...)); -- OK
It should be possible to supply aggregates of subtypes for parameters of the
"full" type. Here, usage of the component name y is really confusing.
****************************************************************
From: Dan Eilers
Sent: Thursday, November 8, 2001 1:26 AM
Christoph Grein wrote:
> A possible complication are named aggregate associations.
>
> What is the name of the component?
>
> Ag_S: rec_subset := (y => e1, i => ...);
> Ag_R: rec (e1) := (x => e1, i => ...);
>
> This is a bit confusing, although with the context of the subtype given,
> might seem OK.
I think the context makes these cases pretty clear.
The aggregates on the right-hand-side should be interpreted
based on the subtype specified on the left-hand-side.
> But what about aggregate parameters of subprograms:
>
> procedure P (X: Rec);
>
> P ((y => e1, i => ...)); -- subtype aggregate, legal?
> P ((x => e1, i => ...)); -- OK
>
> It should be possible to supply aggregates of subtypes for parameters of the
> "full" type. Here, usage of the component name y is really confusing.
The first call should not be legal, and hence no confusion,
since the parameter is declared to be of the subtype Rec,
and there is no rec_subset anywhere in sight to cause confusion.
Of course, if the aggregate was qualified to be of subtype
rec_subset that would make it legal, but would also eliminate
any potential confusion.
****************************************************************
From: Robert Duff
Sent: Tuesday, November 13, 2001 11:02 AM
> I didn't get any comments on the second part of my question.
> Is there a good reason why subtype declarations aren't like
> type declarations in allowing a discriminant part?
It seems very strange to me to have different subtypes of the same type
use different names for the same component.
I agree that it would be desirable to somehow be able constrain
discriminants to be in a range (or even an arbitrary list of values!),
instead of just a single value. But it doesn't seem worth changing at
this point.
****************************************************************