Search code examples
c++pointersgccc++17new-operator

An error is issued by gcc relative to parsing type-id in a new expression


This program

#include <cstddef>

int main()
{
    const std::size_t N1 = 2;
    const std::size_t N2 = 3;

    int ( **p )[N1] = new ( int ( *[N2] )[N1] );
}

does not compile using the compiler C++ gcc HEAD 10.0.0 20190.

The compiler issues error

prog.cc: In lambda function:
prog.cc:8:40: error: expected '{' before ')' token
    8 |    int ( **p )[N1] = new ( int ( *[N2] )[N1] );
      |                                        ^
prog.cc: In function 'int main()':
prog.cc:8:34: error: no match for 'operator*' (operand type is 'main()::<lambda()>')
    8 |    int ( **p )[N1] = new ( int ( *[N2] )[N1] );
prog.cc:8:47: error: expected type-specifier before ';' token
    8 |    int ( **p )[N1] = new ( int ( *[N2] )[N1] );
      |                                               ^

However the program compiles using clang HEAD 10.0.0.

Is the pointer type-id specification ambiguous or is it indeed a gcc bug?

EDIT: By the way if to remove the outer parentheses like

int ( **p )[N1] = new int ( *[N2] )[N1];

then clang also issues an error referencing a lambda

prog.cc:8:38: error: expected body of lambda expression
   int ( **p )[N1] = new int ( *[N2] )[N1];
                                     ^

Solution

  • As far as I can tell, this is definitely a bug in the newest versions of GCC. Presumably, GCC is attempting to parse the ( int ( *[N2] )[N1] ) part as a new-placement, i.e., a parenthesized expression list. Now int ( is interpreted as a function-style cast, etc.

    Per new-expression:

    new-expression:
        ::opt new new-placementopt new-type-id new-initializeropt
        ::opt new new-placementopt ( type-id ) new-initializeropt

    There is no special disambiguation rule regarding new-placement vs ( type-id ), so the thing should be interpreted as a type-id if it cannot be interpreted as an expression-list.


    The EDIT part is a syntax error per [expr.new]/4:

    Note: Parentheses in a new-type-id of a new-expression can have surprising effects. [ Example:

    new int(*[10])();               // error
    

    is ill-formed because the binding is

    (new int) (*[10])();            // error
    

    Instead, the explicitly parenthesized version of the new operator can be used to create objects of compound types:

    new (int (*[10])());
    

    allocates an array of 10 pointers to functions (taking no argument and returning int). — end example ] — end note ]