diff options
author | Alexandre Oliva <oliva@adacore.com> | 2024-11-13 19:21:56 -0300 |
---|---|---|
committer | Marc Poulhiès <dkm@gcc.gnu.org> | 2025-01-03 16:39:12 +0100 |
commit | fbe14f65eddd7f450079cff893a2ddc8fb454543 (patch) | |
tree | 86e6db67e16308de32e6fee3feb85e03186e91bc /gcc/ada/doc/gnat_rm | |
parent | 758de5fed10a7c964bb06a143a3dc3c308721ddd (diff) | |
download | gcc-fbe14f65eddd7f450079cff893a2ddc8fb454543.zip gcc-fbe14f65eddd7f450079cff893a2ddc8fb454543.tar.gz gcc-fbe14f65eddd7f450079cff893a2ddc8fb454543.tar.bz2 |
ada: Handle C++ exception hierarchies
This patch introduces support for defining exceptions in Ada with
C++'s notion of exception type compatibility, such as handling
occurrences of derived types, and obtaining class-wide access to the
thrown/raised objects. As a bonus, it adds support for C++ dependent
(wrapped) exceptions, and introduces types and interfaces to match
C++'s std::type_info and std::exception.
Support for C++ exceptions with base-type matching, added to raise-gcc
by calling subprograms in Ada units, requires these units and their
dependencies to be linked into programs that link with raise-gcc.
gcc/ada/ChangeLog:
* Makefile.rtl (GNATRTL_NONTASKING_OBJS): Add g-cpp, g-cppstd,
and g-cstyin.
* doc/gnat_rm/interfacing_to_other_languages.rst (Interfacing to C++):
Document class-wide matching and new interfaces.
* exp_prag.adb (Expand_Pragma_Import_Or_Interface): Add class-wide
exception matching support with 'B' as language identifier.
* libgnat/a-exexpr.adb (Setup_Current_Excep): Add Id formal.
(Set_Foreign_Occurrence): Likewise.
(Propagate_GCC_Exception): Adjust.
(Set_Exception_Parameter): Likewise.
(Unhandled_Except_Handler): Likewise.
* libgnat/g-cpp.ads: New.
* libgnat/g-cppexc.adb (Raise_Cpp_Exception): Match 'B' lang id.
(Get_Object_Address): New.
(Get_Object): Rewrite.
(Get_Access_To_Object): New.
(Get_Access_To_Tagged_Object): New.
(Get_Type_Info): New.
(Convert_Caught_Object): New.
* libgnat/g-cppexc.ads (Get_Object_Address): New.
(Get_Object): Note the Cpp Convention requirement.
(Get_Access_To_Object): New.
(Get_Access_To_Tagged_Object): New.
(Get_Type_Info): New.
* libgnat/g-cppstd.adb: New.
* libgnat/g-cppstd.ads: New.
* libgnat/g-csclex.ads: New, unused.
* libgnat/g-cstyin.adb: New.
* libgnat/g-cstyin.ads: New.
* libgnat/g-excact.adb (Exception_Language): New.
(Is_Foreign_Exception): Rewrite.
* libgnat/g-excact.ads (Exception_Languages): New.
(Exception_Language): New.
* libgnat/s-stalib.ads (Lang): Document 'B'.
* raise-gcc.c (__gnat_setup_current_excep): Add Exception_Id formal.
(CXX_DEPENDENT_EXCEPTION_CLASS): New.
(cxx_type_info): New.
(__cxa_exception): Rename exceptionType to encompass PrimaryException.
(_GNAT_Exception): Drop wrapper.
(EID_For): Adjust.
(exception_class_eq): Likewise.
(__gnat_exception_language_is_cplusplus): New.
(__gnat_exception_language_is_ada): New.
(__gnat_convert_caught_object): Declare.
(__gnat_get_cxx_dependent_exception): New.
(__gnat_maybe_get_cxx_dependent_exception): New.
(__gnat_get_cxx_exception_type_info): New.
(__gnat_obtain_caught_object): New.
(is_handled_by): Adjust. [!CERT] Add eid formal, handle dependent
exceptions and base-type matches.
(get_action_description_for) [!CERT]: Add eid formal. Adjust.
(personality_body): Adjust.
* gcc-interface/Make-lang.in (GNAT_ADA_OBJS, GNATBIND_OBJS) [!STAGE1]:
Add new g-cpp, g-cppstd, g-cstyin + preexisting g-cppexc
and i-cstrin.
* gnat-style.texi: Regenerate.
* gnat_rm.texi: Regenerate.
Diffstat (limited to 'gcc/ada/doc/gnat_rm')
-rw-r--r-- | gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst | 163 |
1 files changed, 162 insertions, 1 deletions
diff --git a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst index ad0be51..03cd330 100644 --- a/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst +++ b/gcc/ada/doc/gnat_rm/interfacing_to_other_languages.rst @@ -121,7 +121,168 @@ It is also possible to import a C++ exception using the following syntax: The ``External_Name`` is the name of the C++ RTTI symbol. You can then -cover a specific C++ exception in an exception handler. +cover a specific C++ exception in an exception handler. If the string +ends with "'Class", as if referencing the Class attribute of the C++ +type, that enables "class-wide" type matching, i.e., instances of C++ +classes derived from the one denoted by the RTTI symbol, that would be +caught by C++ handlers for that type, will also be caught by Ada +handlers for ``Entity``. For non-class-wide RTTI symbols imported from +C++, only exact type matches will be handled. C++ rethrown (dependent) +exceptions are not distinguishable from the corresponding primary +exceptions: they are handled exactly as if the primary exception had +been raised. + +With imported exceptions, especially with base-type matching, a single +handled_sequence_of_statements may have exception handlers with +choices that cover the same C++ types in ways that GNAT cannot detect. +For example, C++ classes ``base`` and ``derived`` may be imported as +exceptions with base-type matching, but GNAT does not know that they +are related by inheritance, only the runtime will know it. Given: + +:: + + exception + when Derived_Exception => null; + when Base_Exception => null; + when others => null; + +the earliest handler that matches the type of the raised object will +be selected. If an instance of ``derived`` or a further derived type +is raised, the first handler will be used; if an instance of ``base`` +that is not an instance of ``derived`` is raised, the second handler +will be used; raised objects that are not instances of ``base`` will +be handled by the ``others`` handler. However, if the handlers were +reordered (``others`` must remain last), the ``Derived_Exception`` +handler would never be used, because ``Base_Exception`` would match +any instances of ``derived`` before ``Derived_Exception`` or +``others`` handlers were considered. Mixing exact-type and base-type +matching exceptions may also involve overlapping handlers that GNAT +will not reject: an exact-type ``Base_Only_Exception`` handler placed +before ``Base_Exception`` will handle instances of ``base``, whereas +instances of derived types will be handled by +``Base_Exception``. Swapping them will cause ``Base_Exception`` to +handle all instances of ``base`` and derived types, so that a +subsequent handler for ``Base_Only_Exception`` will never be selected. + +The C++ object associated with a C++ ``Exception_Occurrence`` may be +obtained by calling the ``GNAT.CPP_Exceptions.Get_Object_Address`` +function. There are convenience generic wrappers named ``Get_Object``, +``Get_Access_To_Object``, and ``Get_Access_To_Tagged_Object``, +parameterized on the expected Ada type. Note that, for exceptions +imported from C++, the address of the object is that of the subobject +of the type associated with the exception, which may have a different +address from that of the full object; for C++ exceptions handled by +``others`` handlers, however, the address of the full object is +returned. + +E.g., if the imported exception uses the RTTI symbol for the base +class, followed by "'Class", and the C++ code raises (throws) an +instance of a derived class, a handler for that imported exception +will catch this ``Exception_Occurrence``, and ``Get_Object_Address`` +will return the address of the base subobject of the raised derived +object; ``Get_Object``, ``Get_Access_To_Object`` and +``Get_Access_To_Tagged_Object`` only convert that address to the +parameterized type, so the specified type ought to be a type that +imports the C++ type whose RTTI symbol was named in the declared +exception, i.e., base, not derived or any other type. GNAT cannot +detect or report if a type is named that does not match the handler's +RTTI-specified type. + +For ``others`` handlers, and for exact type matches, the full object +is obtained. The ``Get_Type_Info`` function that takes an +``Exception_Occurrence`` argument can be used to verify the type of +the C++ object raised as an exception. The other ``Get_Type_Info`` +function, that takes an ``Exception_Id``, obtains the type expected by +the handler, and no such type exists for ``others`` handlers. +``GNAT.CPP.Std.Name`` can then convert the opaque +``GNAT.CPP.Std.Type_Info_Ptr`` access to ``std::type_info`` objects, +returned by either ``Get_Type_Info`` function, to a C++ mangled type +name. + +If an ``Exception_Occurrence`` was raised from C++, or following C++ +conventions, ``GNAT.Exception_Actions.Exception_Language`` will return +``EL_Cpp``, whether the exception handler is an imported C++ exception +or ``others``. ``GNAT.Exception_Actions.Is_Foreign_Exception`` returns +True for all of these, as well as for any case in which +``Exception_Language`` is not ``EL_Ada``. + +:: + + -- Given the following partial package specification: + + Base_Exception : exception; + pragma Import (Cpp, Base_Exception, "_ZTI4base'Class"); + -- Handle instances of base, and of subclasses. + + type Base is limited tagged record + [...] + end record; + pragma Import (Cpp, Base); + + type Derived is limited tagged record + [...] + end record; + pragma Import (Cpp, Derived); + + type Unrelated is access procedure (B : Boolean); + + function Get_Base_Obj_Acc is + new Get_Access_To_Tagged_Object (Base); + function Get_Derived_Obj_Acc is + new Get_Access_To_Tagged_Object (Derived); + function Get_Unrelated_Obj_Acc is + new Get_Access_To_Object (Unrelated); + + procedure Raise_Derived; + -- Raises an instance of derived (with a base subobject). + + + -- The comments next to each statement indicate the behavior of + -- the following pseudocode blocks: + + begin + Raise_Derived; + exception + when BEx : Base_Exception => + ?? := Is_Foreign_Exception (BEx); -- True + ?? := Exception_Language (BEx); -- EL_Cpp + ?? := Name (Get_Type_Info (BEx)); -- "7derived" + ?? := Name (Get_Type_Info (Exception_Identity (BEx))); -- "4base" + ?? := Get_Object_Address (BEx); -- base subobject in derived object + ?? := Get_Base_Obj_Acc (BEx): -- ditto, as access to Base + ?? := Get_Derived_Obj_Acc (BEx): -- ditto, NO ERROR DETECTED! + ?? := Get_Unrelated_Obj_Acc (BEx): -- ditto, NO ERROR DETECTED! + end; + + + begin + Raise_Derived; + exception + when BEx : others => + ?? := Is_Foreign_Exception (BEx); -- True + ?? := Exception_Language (BEx); -- EL_Cpp + ?? := Name (Get_Type_Info (BEx)); -- "7derived" + ?? := Get_Type_Info (Exception_Identity (BEx)); -- null + ?? := Get_Object_Address (BEx); -- full derived object + ?? := Get_Derived_Obj_Acc (BEx): -- ditto, as access to Derived + ?? := Get_Base_Obj_Acc (BEx): -- ditto, NO ERROR DETECTED! + ?? := Get_Unrelated_Obj_Acc (BEx): -- ditto, NO ERROR DETECTED! + end; + +The calls marked with ``NO ERROR DETECTED!`` will compile sucessfully, +even though the types specified in the specializations of the generic +function do not match the type of the exception object that the +function is expected to return. Mismatches between derived and base +types are particularly relevant because they will appear to work as +long as there isn't any offset between pointers to these types. This +may hold in many cases, but is subject to change with various possible +changes to the derived class. + +The ``GNAT.CPP.Std`` package offers interfaces corresponding to the +C++ standard type ``std::type_info``. Function ``To_Type_Info_Ptr`` +builds an opaque ``Type_Info_Ptr`` to reference a ``std::type_info`` +object at a given ``System.Address``. + .. _Interfacing_to_COBOL: |