[Ada] Improve last exception info availability from C++ handlers
The Most_Recent_Exception service failed to provide accurate information on an
Ada exception caught by a C++ handler for foreign exceptions. The service
relies on updates of a "current exception buffer" from live exception objects
at various points of the propagation process and this update was not performed
early enough for the case of foreign exception handlers in non-Ada handlers.
The correction applied here consists in moving one of the updates earlier in
the raise process, just before unwinding starts, then refine the update API to
prevent a redundant copy during the unwinding search phase for the same
exception.
The example below, compiled with
gcc -c b.cc
gnatmake -g main.adb -largs b.o --LINK=g++
is expected to run and display
ada info:
Checking Most_Recent_Exception for CONSTRAINT_ERROR ... OK!
// b.cc
extern "C" {
void foo ();
extern void _ada_trigger ();
extern void _ada_occurrence_info ();
}
void foo ()
{
try {
_ada_trigger ();
} catch (const abi::__foreign_exception &e) {
printf ("ada info:\n");
_ada_occurrence_info();
}
}
-- main.adb
with EH;
procedure Main is
begin
EH.Foo;
end;
-- eh.adb
with Gnat.Most_Recent_Exception;
with Ada.Text_IO; use Ada.Text_IO;
package body EH is
procedure Ada_Trigger is
begin
raise Constraint_Error;
end;
procedure Ada_Occurrence_Info is
begin
Check_MRE ("CONSTRAINT_ERROR");
end;
function Pre_Check_MRE (Ename : String) return Exception_Id is
MROA : Exception_Occurrence_Access :=
GNAT.Most_Recent_Exception.Occurrence_Access;
begin
Put ("Checking Most_Recent_Exception for " & Ename & " ... ");
if MROA = null then
Put_Line ("Most recent exception occurrence access is NULL");
return Null_Id;
else
return Exception_Identity (MROA.all);
end if;
end;
procedure Diagnose_MRE (MRID : Exception_Id; Ok : Boolean) is
begin
if Ok then
Put_Line ("OK!");
else
Put_Line ("Err, Most_Recent_Exception was " & Exception_Name (MRID));
end if;
end;
procedure Check_MRE (Eid : Exception_Id) is
MRID : Exception_Id := Pre_Check_MRE (Ename => Exception_Name (Eid));
begin
Diagnose_MRE (MRID, Ok => Eid = MRID);
end;
procedure Check_MRE (Ename : String) is
MRID : Exception_Id := Pre_Check_MRE (Ename => Ename);
begin
Diagnose_MRE (MRID, Ok => Ename = Exception_Name (MRID));
end;
end;
-- eh.ads
with Ada.Exceptions; use Ada.Exceptions;
package EH is
procedure Ada_Trigger with
Export, Convention => C, External_Name => "_ada_trigger";
procedure Ada_Occurrence_Info with
Export, Convention => C, External_Name => "_ada_occurrence_info";
procedure Foo with Import, Convention => C, External_Name => "foo";
procedure Check_MRE (Eid : Exception_Id);
procedure Check_MRE (Ename : String);
end;
2018-06-11 Olivier Hainque <hainque@adacore.com>
gcc/ada/
* libgnat/s-excmac*.ads: Factorize Unwind_Action definitions ...
* libgnat/a-exexpr.adb: ... Here, then add comments describing the
major datastructures associated with the current exception raised.
(Setup_Current_Excep): Accept a "Phase" argument conveying the
unwinding phase during which this subprogram is called. For an Ada
exception, don't update the current exception buffer from the raised
exception object during SEARCH_PHASE, as this is redundant with the
call now issued just before propagation starts.
(Propagate_GCC_Exception): Move call to Setup_Current_Excep ahead of
the unwinding start, conveying Phase 0.
(Unhandled_Except_Handler): Pass UA_CLEANUP_PHASE as the Phase value on
the call to Setup_Current_Excep.
* raise-gcc.c (personality_body): Pass uw_phases as the Phase argument
on calls to Setup_Current_Excep.
From-SVN: r261426