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

Richard Smith richardsmith at google.com
Wed Oct 12 21:09:02 UTC 2016


On 12 October 2016 at 13:51, John McCall <rjmccall at apple.com> wrote:

> On Oct 12, 2016, at 11:58 AM, Richard Smith <richardsmith at google.com>
> wrote:
> On 11 October 2016 at 19:20, John McCall <rjmccall at apple.com> wrote:
>
>> On Oct 11, 2016, at 4:20 PM, Richard Smith <richardsmith at google.com>
>> wrote:
>> 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.
>>
>>
>> Are there any implementations which actually plan to throw out the
>> dynamic exception specification matching logic?
>>
>
> *shrug* Maybe MSVC? Any conforming C++17 implementation will need to
> demote that side of their enforcement to a warning. And I think there are
> NB comments for C++17 proposing that we apply http://www.open-std.org/
> jtc1/sc22/wg21/docs/papers/2016/p0003r4.html for C++17 rather than
> waiting for C++20.
>
>
> Not enforcing the old rules is also compatibility-breaking, of course,
> because of SFINAE.
>
> 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. =(
>>
>>
>> Hmm, that would work pretty well for this case.
>>
>> 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).
>>
>>
>> Well, recall that noexcept function types have always been writable; they
>> just didn't necessarily get enforced reliably.  Also, noexcept and throw()
>> are pretty popular, and aren't there proposals to infer them in more cases?
>>
>
> Proposals, yes, but nothing in C++17.
>
>
> I think it's reasonable to anticipate that when judging how often
> functions will be noexcept.
>
>
> It's really hard to say abstractly how much impact this will have.
>> There's a lot of potential for breakage, but it's also quite possible that
>> there won't be many changes and that almost all of them will be lost in the
>> great grey expanse of C++ binary compatibility.
>>
>
> We'll have an implementation soon, and then we can find out whether this
> is a problem in practice.
>
>
> I'll admit that I don't attend committee meetings, but I thought that
> implementation experience was expected *prior* to standardization, not
> something that gets done months after voting the thing in concurrently with
> the committee finalizing the language in a draft for next year's release.
>

Some of us try to push for that. So far we've not had much success.

> 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.
>>
>>
>> Okay, so it only triggers SFINAE failures in nested function types, and
>> you can't overload templates by it?  I agree that that helps a lot.
>>
>> 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?
>>
>>
>> I hadn't realized that the expression logic was so consistent about
>> defining e.g. the behavior of the conditional operator on l-values in terms
>> of reference binding.  I apologize.
>>
>> ...I see that this adds a new special case to exception handling.
>>
>
> Yes; I'd forgotten to mention this side of the ABI change.
>
> We'll also need a new flag on type_info objects to model this. In line
> with the transaction_safe changes that Jason proposed, I suggest adding a
> __noexcept_mask = 0x40 to __pbase_type_info, and representing a pointer to
> noexcept function as a pointer with __noexcept_mask bit set to the
> corresponding *non-noexcept* function pointer type.
>
>
> I strongly disagree; we should take this opportunity to revisit that
> decision.  The floodgates are open, and this set of function type
> attributes is clearly going to grow over time.  (It's actually
> transaction_safe that really drives this point home; noexcept is at least a
> longstanding part of the core language in various forms.)  We also have a
> lot of vendor-specific function type attributes that are part of the type
> but just aren't standardized and can't be represented in type_info.  I
> don't think it makes sense to indefinitely keep hacking these things into
> the pointer type flags; we should just bite the bullet and create a new
> function_type_info subclass.
>

OK. How about this:

class __qualified_function_type_info : public __function_type_info {
public:
  const __function_type_info *__base_type;
  unsigned int __qualifiers;
  enum __qualifiers_mask {
    __const_mask = 0x01,
    __volatile_mask = 0x02,
    __restrict_mask = 0x04,
    __lval_ref_mask = 0x08,
    __rval_ref_mask = 0x10,
    __noexcept_mask = 0x20,
    __transaction_safe_mask = 0x40
  };
};

... where __base_type is the unqualified function type, included to avoid
the need for string comparisons when checking for a matching exception
handler. The base class __function_type_info would be used for types with
no qualifiers.

It might also be reasonable to reserve a bit for 'noreturn', since several
compilers treat it as part of the function type in some way.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://sourcerytools.com/pipermail/cxx-abi-dev/attachments/20161012/fb0ef081/attachment.html>


More information about the cxx-abi-dev mailing list