When does ~decltype(expr) make sense ?

Hi,

I am trying to fix this bug,
http://llvm.org/bugs/show_bug.cgi?id=22508

In short,
struct A
{
~decltype(auto){}
};

is accepted when it should not be.

In Parser::ParseUnqualifiedId (ParseExprCXX.cpp:2488 ),

// Parse the ‘~’.
SourceLocation TildeLoc = ConsumeToken();

if (SS.isEmpty() && Tok.is(tok::kw_decltype)) {
DeclSpec DS(AttrFactory);
SourceLocation EndLoc = ParseDecltypeSpecifier(DS);
if (ParsedType Type = Actions.getDestructorType(DS, ObjectType)) {
Result.setDestructorName(TildeLoc, Type, EndLoc);
return false;
}
return true;
}

After this, clang handles the identifier case.

Removing this block results in clang reporting the correct diagnostic:
“error: expected a class name after ‘~’ to name a destructor”

Why is this block present ?

Can anyone point out what is the expected behavior ?

Thanks

Manasij Mukherjee

Hi,
I am trying to fix this bug,
http://llvm.org/bugs/show_bug.cgi?id=22508

In short,
struct A
{
    ~decltype(auto){}
};
is accepted when it should not be.

In Parser::ParseUnqualifiedId (ParseExprCXX.cpp:2488 ),

// Parse the '~'.
    SourceLocation TildeLoc = ConsumeToken();

    if (SS.isEmpty() && Tok.is(tok::kw_decltype)) {
      DeclSpec DS(AttrFactory);
      SourceLocation EndLoc = ParseDecltypeSpecifier(DS);
      if (ParsedType Type = Actions.getDestructorType(DS, ObjectType)) {
        Result.setDestructorName(TildeLoc, Type, EndLoc);
        return false;
      }
      return true;
    }

After this, clang handles the identifier case.

Removing this block results in clang reporting the correct diagnostic:
"error: expected a class name after '~' to name a destructor"

Why is this block present ?
Can anyone point out what is the expected behavior ?

5.1.1 [expr.prim.general] in paragraph 8 states that "The form ~
decltype-specifier also denotes the destructor, but it shall not be used as
the unqualified-id in a qualified-id."

I implemented this a while ago, but it looks like (r146155) it was for
expressions (x.~decltype(*x)(), for example) not necessarily for dtor
declarations.

I can't find any particular words in the spec about taht, but I could
believe it's not intended (I didn't test it, at least) for dtor
declarations/definitions to use this syntax.

http://llvm.org/bugs/show_bug.cgi?id=15801 was also filed about this

[class.dtor] (12.4)/1:

"A declaration of a destructor uses a function declarator (8.3.5) of the form

ptr-declarator ( parameter-declaration-clause ) exception-specificationopt attribute-specifier-seqopt

where the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:

– in a member-declaration that belongs to the member-specification of a class but is not a friend declaration (11.3), the id-expression is ~class-name and the class-name is the injected-class-name (Clause 9) of the immediately-enclosing class;

[… bullets 2 and 3 do not apply …]"

A decltype-specifier is never a class-name, so ~decltype(…) cannot be used to declare a destructor.

How about the second case in the bug report ?

struct A {
~A(){}
};
void foo(A* a) {
a->~decltype(A())();
}

This does not seem valid to me.

There is a test (CXX/special/class.dtor/p10-0x.cpp) that expects this construct to build properly.

If both of these are not valid, does removing the if block I mention solve the issue?

Or does this bug go deeper ?

5.1.1 [expr.prim.general] in paragraph 8 states that "The form ~
decltype-specifier also denotes the destructor, but it shall not be used as
the unqualified-id in a qualified-id."

I implemented this a while ago, but it looks like (r146155) it was for
expressions (x.~decltype(*x)(), for example) not necessarily for dtor
declarations.

I do not clearly understand what this sentence implies.

Could you elaborate?

Also, if the meaning is as you interpreted it, what is the rationale for
allowing x.~decltype(*x)() ?

Not quite sure I understand this question (though I did make a mistake in
that example, should've been x->~decltype(*x)())

5.1.1 talks about how ~decltype(...) is a valid unqualified id, except in
the case of a qualified id (ie, you can't write "x::y::~decltype(...)"). So
that's how x->~decltype(*x)() is valid, as far as I see/read/understand it
- wherever you can use an unqualified-id that would name a dtor, you can
use ~decltype(...) too (ecxept in the qualified-id case).

5.1.1 [expr.prim.general] in paragraph 8 states that "The form ~
decltype-specifier also denotes the destructor, but it shall not be used as
the unqualified-id in a qualified-id."

I implemented this a while ago, but it looks like (r146155) it was for
expressions (x.~decltype(*x)(), for example) not necessarily for dtor
declarations.

I do not clearly understand what this sentence implies.

Could you elaborate?

Also, if the meaning is as you interpreted it, what is the rationale for
allowing x.~decltype(*x)() ?

Not quite sure I understand this question (though I did make a mistake in
that example, should've been x->~decltype(*x)())

That doesn't work either: decltype(*x) is a reference type. It'd need to be
something ridiculous like

  X x;
  x.~decltype(x)();
  new (&x) X;

5.1.1 talks about how ~decltype(...) is a valid unqualified id, except in

the case of a qualified id (ie, you can't write "x::y::~decltype(...)"). So
that's how x->~decltype(*x)() is valid, as far as I see/read/understand it
- wherever you can use an unqualified-id that would name a dtor, you can
use ~decltype(...) too (ecxept in the qualified-id case).

Right; you can use ~decltype(...)() to invoke a destructor, but you can't
use ~decltype to declare a destructor.

So, an extra condition takes care of not allowing the decltype when declaring the dtor.
! getCurScope()->isClassScope()

Am I correct ?

you can use ~decltype(…)() to invoke a destructor

Why is that allowed?

And what exactly is a valid use of this ?

From the report,
A().~decltype(auto); // ICE, and A can be an empty struct

Does the standard say anything about what can the decltype contain ?

Should the compiler figure something out from the auto here ?

So, an extra condition takes care of not allowing the decltype when
declaring the dtor.
! getCurScope()->isClassScope()

Am I correct ?

No; ~decltype can appear in class scope in default arguments, default
member initializers, initializers for static data members, template
arguments, and so on.

The bug is due to confusion over the contract of Sema::getDestructorType.
It is assuming that a null ObjectType implies that an error has already
been produced, but ParseUnqualifiedId calls it with a null ObjectType to
mean simply that no ObjectType was provided. One side of this interface is
wrong; we need to pick which one, and produce a diagnostic on that side if
ObjectType is in fact null.

(We should be using TypeResult here rather than ParsedType, to make it
clear whose responsibility it is to produce the diagnostic.)

One side of this interface is wrong

Any reasons for handling this in Sema instead of Parser?

The following (in ParseUnqualifiedId) seems to work:

if (ObjectType.get().isNull()) {
Diag(Tok, diag::err_destructor_tilde_identifier);
return true;
}

if (DS.getTypeSpecType() == DeclSpec::TST_decltype_auto) {
Diag(Tok, diag::err_destructor_tilde_identifier);
//needs a better diag. which one ?
return true;
}

The first one handles the declaration of dtor using decltype.

But the assertion failure for
A().~decltype(auto);

remains.

The second one handles that.