Search code examples
c++language-lawyerc++23

Pointer to abominable function type? Compiler bug?


According to C++23 what is FTDecay<int()&>?

template <class Signature>
struct Decompose;

template <class T>
struct Decompose<void(T)> {
    using Type = T;
};

template <class T>
using FTDecay = typename Decompose<void(T)>::Type;


FTDecay<int()&> x = "Hello";

In current MSVC, Clang and GCC implementations the last line above generates error messages that mention an ill-formed type int (*)() & (demo). GCC says:

error: cannot convert 'const char*' to 'FTDecay<int() &>' {aka 'int (*)() &'}
       in initialization

P0172r0 says it is ill-formed (diagnostic required) to form a reference or a pointer to an abominable function type. The paper introduces this term; it means a function type followed by a cv-ref qualifier. Accordingly, using X = int (*)() &; is rejected by all compilers (demo).


Solution

  • [dcl.fct]p5

    1. [...] After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. [...]

    [dcl.ptr]p4

    1. [Note 1: [...] Forming a function pointer type is ill-formed if the function type has cv-qualifiers or a ref-qualifier; see [dcl.fct]. [...]]

    This is a non-normative note, but is based on [dcl.fct]p10

    1. A function type with a cv-qualifier-seq or a ref-qualifier (including a type named by typedef-name ([dcl.typedef], [temp.param])) shall appear only as:
      • the function type for a non-static member function,
      • the function type to which a pointer to member refers,
      • the top-level function type of a function typedef declaration or alias-declaration,
      • the type-id in the default argument of a type-parameter ([temp.param]), or
      • the type-id of a template-argument for a type-parameter ([temp.arg.type]).

    ... Where none of those bullets are "the type pointed to by a pointer type" or similar.


    Therefore, void(T) adjusts T which has function type void() & to "pointer to void() &", which is ill-formed.

    This would be a SNIFE-able error, but FTDecay<int()&> is ill-formed because Decompose<void(T)> is ill-formed. This is a compiler bug in Clang and GCC that a pointer to ref-qualified function type is formed in the first place and a bug in MSVC that the error isn't early enough (it seems to just pick the default specialization, and doesn't adjust the function type to a pointer to make it an error: https://godbolt.org/z/M9nEzoeET)