Am 12.08.2026 um 13:41 schrieb Arrigo Marchiori:
Hello Peter, All,

On Wed, Aug 12, 2026 at 09:55:47AM +0000, Peter Kovacs wrote:

Hi Arrigo,

Am 12. August 2026 09:04:38 MESZ schrieb Arrigo Marchiori<[email protected]>:
Dear Peter, All,

On Tue, Aug 11, 2026 at 07:58:27AM +0200,[email protected] wrote:

Hi Jim, all,


I switched a bit the target, and try to do a minimal change raise to Win 10
with modern compilers.
As cheap as possible.

I found following issue:

*An override may not make a weaker promise about throwing than the thing it
overrides* — because a caller holding a base pointer was told "this never
throws" and may rely on it.
IMHO we should just get rid of all "throw" specifiers on methods and
functions.
Good find. I will check and remove if possible.
When I briefly looked into this issue, it seemed like a massive edit.

Not only there are lots of methods and functions that are using the
specifiers, but the IDL files are also compiled to use them.

This does not mean it will be impossible... but we all developers will
need to coordinate on that.
here is the answer of my AI work. I hope it helps. I will continue with this sentiment.

+1 on the direction, with one amendment and one caveat about scope.

I've been building the tree with a modern MSVC (VS2019 + Windows 10 SDK) on a
branch, so I have some numbers rather than just opinions.

1. throw(X) -- agreed, remove them
------------------------------------------------------------------------
No argument from me. MSVC has never enforced them, and on GCC/Sun/SGI they
do not even exist: SAL_THROW() expands to nothing there, so roughly 4,500 of
these specifications have been absent on those platforms for the entire life
of the product. They are removed in C++17.

They are also not merely dead weight. Three destructors carrying
throw(RuntimeException) -- ~OWeakObject, ~OWeakAggObject, ~OComponentHelper --
produce 6,690 compile errors across 25 modules and 242 classes on a modern
MSVC. So this is a live blocker, not a tidy-up.

2. throw() is a different thing -- please do not lump it in
------------------------------------------------------------------------
Counted in this tree:

    SAL_THROW( () )        3,677 uses
    SAL_THROW( (Type) )      827 uses  (+ ~32,600 written literally)

MSVC ignores throw(X) but it does honour throw() -- it is __declspec(nothrow),
with real consequences for codegen: elided unwind paths, and a terminate-on-
throw contract that is worth having on acquire()/release() and on destructors.

C++17 removes throw(X) but keeps throw() as a deprecated spelling of noexcept.
C++20 removes it.

So deleting the 3,677 loses something the compiler actually implements. The
right move for those is to migrate them to noexcept, not to delete them. That keeps the guarantee and is the forward-compatible spelling. Worth noting that
GotW #82 predates noexcept -- Sutter's objection is to the runtime-checked
unexpected() machinery, which noexcept does not have.

Suggested as two separate changes:
    throw(X) -> nothing
    throw()  -> noexcept

3. Scope: this is a codemaker change, and it cannot be incremental
------------------------------------------------------------------------
The specifications are generated. InterfaceType::dumpExceptionSpecification()
in codemaker/source/cppumaker/cpputype.cxx writes " throw (" onto every UNO
interface method, so every generated .hpp carries them.

That forces the change to be atomic across the generated/hand-written boundary.
For a non-destructor virtual function, an override with no specification may
throw anything -- which is LESS restrictive than a base declaring
throw(RuntimeException), and therefore ill-formed. Strip them from hand-written
overrides while the generated bases still have them and you simply trade one
set of errors for another.

So cppumaker and the sources have to move in the same commit. I mention it
because "start removing them a module at a time" is the natural first instinct
and it does not work.

4. The good news on compatibility
------------------------------------------------------------------------
Exception specifications do not participate in name mangling, so this is not
an ABI break.

It is also backward compatible for extensions: once the bases lose their
specifications, a third-party component that still declares
throw(RuntimeException) on its overrides is merely more restrictive, which is
legal. External UNO components keep compiling unchanged.

5. The argument I would lead with
------------------------------------------------------------------------
Beyond "deprecated syntax", the specifications now assert something untrue.

Since C++11 a destructor with no written specification is implicitly noexcept.
So any class inheriting from a UNO base (~OWeakObject, declared
throw(RuntimeException)) and from an ordinary base (implicitly non-throwing)
claims both "may throw" and "will not throw" about destroying the same object. There is no annotation that resolves this -- writing the modern noexcept(false)
produces the identical error. The compiler is reporting an incoherence.

And the bodies of all three of those destructors are {}. The specification
described a possibility the implementation never took.

Reply via email to