> On Oct 11, 2016, at 2:11 PM, Richard Smith <[email protected]> wrote:
> Under
>   http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0012r1.html 
> <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.

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; };

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.

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

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.
_______________________________________________
cxx-abi-dev mailing list
[email protected]
http://sourcerytools.com/cgi-bin/mailman/listinfo/cxx-abi-dev

Reply via email to