Version 1.2 of acs/ac-00094.txt

Unformatted version of acs/ac-00094.txt version 1.2
Other versions for file acs/ac-00094.txt

!standard B.01(46)          04-02-27 AC95-00094/01
!class confirmation 04-02-27
!status received no action 04-02-27
!status received 04-02-26
!subject Exception raised in wrong place
!summary
!appendix

From: Peter Hermann
Sent: Thursday, February 26, 2004  2:42 AM

In the following strongly reduced code sequence I demonstrate
an unexpected and not welcomed nasty effect of
an exception raised in the wrong place.
It may possibly be an effect of "call by name"/"call by reference".
Do I have to suffer an Ada language disadvantage
or is this a Gnat compiler weakness?
In any case, I would be greatful for a workaraound.

------------------------------------------------------------------------
with ada.text_io; use ada.text_io;
procedure trap is
  buffer : string(1..256);
  last:integer;
  matrikel7h : string(1..7);
  matrikelast : natural range 7..7;
procedure dialog(prompt : string; result : out string;
                                  resulast : out natural) is
begin
  loop
    put_line (prompt);
    get_line(buffer,last);       -- last other than 7     e.g. < 7
    zwang: begin
      result:=string'(result'range=>' ');
      result(1..last):=buffer(1..last); --raises fine for last>7
      resulast:=last;            -- expected raise of exception (wanted)
      exit;
    exception when others =>
      put_line("***rejected " & buffer(1..last));
    end zwang;
  end loop;
end dialog;
begin -- trap main program

  dialog("7-stellige Matrikelnummer",matrikel7h,matrikelast);
  exception when others =>
    put_line("raised exception after return :-(");
end trap;

****************************************************************

From: Randy Brukardt
Sent: Thursday, February 26, 2004  5:06 PM

The exception is raised exactly where it is supposed to be raised. The out
parameter has subtype Natural, and certainly a value in 0 .. 6 is a legal
value of Natural. Elementary types are by-copy types, and the subtype check
for 7..7 occurs when the result is copied back to the actual parameter.
There's no logic problem with the language semantics.

If you think about it, it couldn't be any other way. First, a prime
principle of Ada is the contract, and the contract for the out parameter is
subtype Natural. That means that you can use the parameter as a value of
Natural within the subprogram without worrying about the subtype of the
actual. (Yes, by-reference types with defaulted discriminants differ in
this - a nod to pragmatics, I think, and one that causes no end of
implementation and usage trouble.) Second, the implementation of the check
that you're expecting would be relatively expensive, as it would require
passing in the subtype bounds with every in out and out parameter. That
would triple the parameter passing overhead and make Ada incompatible with
every other language out there (and in particular make even well-written C
code unable to match Ada semantics). It would be a sure-fire way to make Ada
live up to its (unfair) reputation for being slow.

In any case, I'm not sure why you posted this here; this isn't a place to
post bug reports about compilers!

> In any case, I would be greatful for a workaraound.

Without knowing what it is that you are trying to do, it's impossible to
suggest much. I do think it is dubious to depend on the raising of
Constraint_Error as part of the normal functioning of your application.
That's especially true for range checks, which are trivially written in the
rare case where they are needed. They are best reserved for finding program
bugs - in which case, where the exception is raised does not matter.

Thus, the obvious workaround to explicitly do what you were expecting Ada to
do for you: pass the bound(s) into the subprogram and check them yourself.

****************************************************************

From: Peter Hermann
Sent: Friday, February 27, 2004  4:14 AM

Randy,
I have not much of an objection against your reasoning.
Indeed I thank you for your valuable elaboration.
From the point of view of the subprogram it MAY BE in fact
on the pragmatic side to postpone the raising of the exception
towards the return jump, especially when
1. the formal parameter further changes its value
2. the subprogram is at library level.
In any case I am feeling not comfortable as an ordinary user
of the Ada language to be dependent on parameter mechanisms.

On Thu, Feb 26, 2004 at 05:05:45PM -0600, Randy Brukardt wrote:
> In any case, I'm not sure why you posted this here; this isn't a place to
> post bug reports about compilers!

As you have proven by your reply it is a question of basic principles.

> > In any case, I would be greatful for a workaraound.

I found a workaraound myself (see far below).
Justification:

> Thus, the obvious workaround to explicitly do what you were expecting Ada to
> do for you: pass the bound(s) into the subprogram and check them yourself.

This is of course a working solution albeit ancient way of Fortran.

I am usually against misuse of exception handling in normal logics.
However in exceptionally wrong user data I prefer this elegant
handling method in order to have smaller and more readable code.

Peter Hermann

ps.:
The following work_around is unfortunately not possible
when the subprogram is on library level  )-:
[--*** indicate differences to subprogram trap]
------------------------------------------------------------------------
with ada.text_io; use ada.text_io;
procedure trap2 is
  buffer : string(1..256);
  last:integer;
  matrikel7h : string(1..7);
  subtype seven is integer range 7..7;                  --***
  matrikelast : seven; --replacing: natural range 7..7; --***
procedure dialog(prompt : string; result : out string;
                                  resulast : out seven) --***
 is begin
  loop
    put_line (prompt);
    get_line(buffer,last);       -- last other than 7
    zwang: begin
      result:=string'(result'range=>' ');
      result(1..last):=buffer(1..last);
      resulast:=last;            -- expected raise of exception (wanted)
      exit;
    exception when others =>
      put_line("errmsg: rejected " & buffer(1..last));
    end zwang;
  end loop;
end dialog;
begin -- trap2 main program

  dialog("7-stellige Matrikelnummer",matrikel7h,matrikelast);
  exception when others =>
    put_line("raised exception after return :-(");
end trap2;

****************************************************************

From: Peter Hermann
Sent: Friday, February 27, 2004  4:34 AM

No, it's not a work-around...     :-(

I beg your pardon

****************************************************************

Questions? Ask the ACAA Technical Agent