[cxx-abi-dev] manglings for exception specifications in function types

Richard Smith richardsmith at google.com
Tue Oct 11 23:20:31 UTC 2016


On 11 October 2016 at 15:17, John McCall <rjmccall at apple.com> wrote:

> On Oct 11, 2016, at 2:11 PM, Richard Smith <richardsmith at google.com>
> wrote:
> Under
>   http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html
>
> the noexceptness of a function type is now part of the type. As a result,
> we need manglings for exception-specifications on function
> pointer/reference types:
>
> void f(void()) {}
> void f(void() noexcept) {} // ok, overload not redefinition
>
> (It's not clear to me whether or not this was also necessary prior to
> C++17 to handle dependent exception specifications that appear lexically
> within the parameter list of a function template, and actual implementation
> practice varies as to whether such exception specifications are SFINAEable.)
>
>
> In order to handle overloading/SFINAE on exception specifications in
> dependent cases, we need to be able to mangle not only "noexcept", but also
> "noexcept(expression)" and "throw(<types>)". Suggestion for manglings:
>
> <exception-spec> ::=
>   nx  -- non-throwing exception specification
>   nX <expression> E  -- computed (value-dependent) noexcept
>   tw <type>* E  -- throw (types)
>
> <function-type> ::= [<CV-qualifiers>] [<exception-spec>] [Dx] F [Y]
> <bare-function-type> [<ref-qualifier>] E
>
> In the case of throw(a, b, c), we could omit types that are neither
> instantiation-dependent nor pack expansions (if that omits all types, we
> can use the 'nx' mangling instead), since C++17 says you can't overload on
> the actual types in the dynamic exception specification, and we otherwise
> only need them to be present if they might result in a substitution failure.
>
> Thoughts?
>
>
> I think this is an amazingly late change to the language with pretty thin
> justification; does that count?
>
> This really is a major change which can reasonably be expected to cause
> substantial source and binary breakage.  The proposal mentions
> transaction_safe as a feature that added similar complexity, but that
> analogy is weak because (1) TM is expected to be an optional TS, whereas
> noexcept is a mandatory core language feature, and (2) existing code does
> not use the transaction_safe attribute, whereas noexcept and throw() have
> seen widespread adoption, in the latter case for years.
>
> If it is a goal of this proposal to eliminate the underspecified fake type
> system around exception specifications, it is worth noting that it
> completely fails to do so, since the checking rules for direct function
> pointer assignments are still quite a bit stronger than those provided by
> the new type system.
>

That was indeed a goal here. Can you expand on how it fails? Ignoring the
(deprecated) dynamic exception specifications, this new approach seems
stronger than the old type system, since it works for function types being
arbitrarily nested within other types, not just one level deep within
function types and pointers.

Furthermore, while the proposal does mention a fairly unlikely problem
> arising with template argument deduction, it fails to note the much larger
> one which is likely to break (or cause silently possible-miscompiles in)
> many metaprogramming systems where suddenly function types have acquire an
> entire new axis of differentiation.  For example, this code only
> type-checks because of special rules allowing a conversion:
>
> template <class R, class... A> void take_fn(R (*fn)(A...));
> ...
> extern void my_fn() noexcept;
> take_fn(my_fn);
>
> But, of course, a metaprogram inspecting a function type will completely
> fail to recognize a noexcept function type:
>
>   template <class R, class... A> struct function_result<R(A...)> { using
> type = R; };
>
> And in fact, this adds yet another dimension to the combinatorial
> explosion of specializations required in order to match all function types:
>
>   template <class R, class... A> struct function_result<R(A...) > { using
> type = R; };
>   template <class R, class... A> struct function_result<R(A...) const> {
> using type = R; };
>   template <class R, class... A> struct function_result<R(A...) volatile>
> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) const
> volatile> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) &> { using
> type = R; };
>   template <class R, class... A> struct function_result<R(A...) const &> {
> using type = R; };
>   template <class R, class... A> struct function_result<R(A...) volatile
> &> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) const
> volatile &> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) &&> {
> using type = R; };
>   template <class R, class... A> struct function_result<R(A...) const &&>
> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) volatile
> &&> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) const
> volatile &&> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept >
> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> const> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> volatile> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> const volatile> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> &> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> const &> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> volatile &> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> const volatile &> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> &&> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> const &&> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> volatile &&> { using type = R; };
>   template <class R, class... A> struct function_result<R(A...) noexcept
> const volatile &&> { using type = R; };
>

Hmm, I thought we had added a rule to allow B to be deduced in

  template<class R, class ...A, bool B> void f(R(A...) noexcept(B));

but it looks like we actually didn't. =(

Yes, the above is a problem, if noexcept function types start to appear in
existing code (for instance through use of decltype or by code that passes
around noexcept function pointers).

You will note that I have omitted the necessary specializations for
> "transaction_safe", as well as the incredibly common extension of
> specialized calling conventions.
>
> This also breaks source compatibility for template matching, and basically
> every function template in the standard library is going to change
> manglings (and become *much* larger) due to noexcept expressions now being
> mangled.
>

It's a problem, but I don't think it's as bad as you claim. The mangling of
a function still wouldn't include its exception specification; this would
only affect mangling in cases where a parameter or return type or template
argument involves a function type with an exception-specification -- a lot
less common than every function template in the standard library, but this
still does change manglings for existing code.


> And the entire proposal seems to have forgotten about
> reference-to-function types.
>

The change to [dcl.init.ref]p4 allows a reference to non-noexcept function
to bind to a noexcept function, and this indirectly allows the same during
overload resolution, casts, and so on. What additional considerations were
missed?

But if we're just talking about manglings, then yes, I think your ABI
> proposal is basically fine. :)  It's a little unfortunate to include this
> kind of discrimination so early in the mangling, because some object/image
> file symbol tables optimize for symbols with common prefixes, but our
> mangling scheme is generally poor at achieving that anyway.
>
> John.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://sourcerytools.com/pipermail/cxx-abi-dev/attachments/20161011/ff677535/attachment-0001.html>


More information about the cxx-abi-dev mailing list