ElaboratedType, QualifiedNameType and TypenameType.

Hello.

We were looking into these three AST classes (while wearing the glasses of applications working at the source code level) and we ended up with the following picture:

1) An ElaboratedType is a type name prefixed by a tag keyword.
The current clang implementation requires that this tag keyword is taken from the set { struct, class, union, enum }. This differs from the standard (C++98 7.1.5.3), where also the keyword 'typename' is allowed.
The (optional) name qualifier is not directly encoded in the AST node.

2) A QualifiedNameType is a non-dependent type name provided with a (mandatory) name qualifier. The current implementation does not encode the (optional) tag keyword, but there is a FIXME noting that, e.g., the 'typename' keyword could be used even though the name is not dependent (C++98 14.6p5).

3) A TypenameType is a dependent type name provided with a (mandatory) name qualifier. The current implementation does not encode the (mandatory) tag keyword, which will be tipically be 'typename', but could also be another one, as in the following example:

Hello.

We were looking into these three AST classes (while wearing the glasses
of applications working at the source code level) and we ended up with
the following picture:

1) An ElaboratedType is a type name prefixed by a tag keyword.
The current clang implementation requires that this tag keyword is taken
from the set { struct, class, union, enum }. This differs from the
standard (C++98 7.1.5.3), where also the keyword 'typename' is allowed.
The (optional) name qualifier is not directly encoded in the AST node.

The C++98/03 standard is very, very confusing here. The intent was not to make 'typename' abide by the rules in 7.1.5.3 [dcl.type.elab]; it was a syntactic convenience in the grammar.

I suggest that you instead look at C++0x, which has a "typename-specifier" grammar production for typename types that is completely distinct from elaborated-type-specifiers.

2) A QualifiedNameType is a non-dependent type name provided with a
(mandatory) name qualifier. The current implementation does not encode
the (optional) tag keyword, but there is a FIXME noting that, e.g., the
'typename' keyword could be used even though the name is not dependent
(C++98 14.6p5).

The intent of QualifiedNameType is that it never have a tag; that should be handled by ElaboratedType.

3) A TypenameType is a dependent type name provided with a (mandatory)
name qualifier. The current implementation does not encode the
(mandatory) tag keyword, which will be tipically be 'typename', but
could also be another one, as in the following example:

[snip]

We would like to improve things in order to have an exact picture of
what was written in the program. We have two options:

A) allow for 'typename' in ElaboratedType and let these keywords be
*always* encoded by ElaboratedType nodes. That is, variable x1, x2, y1
and y2 will all have ElaboratedType, containing a TypenameType (x1 and
y1) or a QualifiedNameType (x2 and y2). One may also consider the
possibility of renaming class "TypenameType" to become
"DependentNameType" (since the typename keyword is not actually implied).

B) get rid of ElaboratedType nodes and push the corresponding info about
the tag keyword down into QualifiedNameType (where the name qualifier
would become optional) and TypenameType.

To our eyes, option A) is closer to what is said in the C++ standard;
however, option B would maybe require less AST nodes (of course, the
inner nodes would become bigger).
What is the opinion of clang developers?
Is there an option C which is even better?

There is an option (C). Here's what I think each of the types should do:

ElaboratedType: Should handle all elaborated-type-specifiers in the C++0x sense, which means a type referenced via the enum/class/struct/union keyword. This class should be extended with an optional NestedNameSpecifier to handle qualified elaborated-type-specifiers, ideally in a implementation-detail base class so that we don't pay a size penalty for "struct X". Getting this right should make fixing PR5681 trivial.

QualifiedNameType: Should handle all type references that involve qualified names but for which we can resolve the name lookup to actually refer to a specific type. These could actually have "typename" qualifiers on them, but the point is that QualifiedNameType is always sugar.

TypenameType: Should handle all type references that involve qualified names where the name it refers to is a dependent name. May or may not actually have the "typename" keyword.

  - Doug

There is an option (C). Here's what I think each of the types should
do:

ElaboratedType: Should handle all elaborated-type-specifiers in the
C++0x sense, which means a type referenced via the
enum/class/struct/union keyword. This class should be extended with
an optional NestedNameSpecifier to handle qualified
elaborated-type-specifiers, ideally in a implementation-detail base
class so that we don't pay a size penalty for "struct X". Getting
this right should make fixing PR5681 trivial.

Why don't have ElaboratedType of QualifiedNameType instead of optional NNS?

It seems to me it would be more congruent:

1) ElaboratedType of EnumType (for non qualified enum X)
2) ElaboratedType of QualifiedNameType of EnumType (for qualified not
dependent enum A::X)
3) ElaboratedType of TypenameType (for qualified dependent enum A::X)

The problem is that elaborated-type-specifiers in the C++ language have very different name-lookup semantics than QualifiedNameType/TypenameType (C++ [basic.lookup.elab]), so it's cleaner to have the *Type nodes match up with language constructs. That's actually what

  http://llvm.org/bugs/show_bug.cgi?id=5681

is about: we're using ElaboratedType as an outer level of sugar (more like what you're proposing than what I'm proposing), and because of that we get name-lookup wrong when we instantiate the node that the ElaboratedType points to. Instead, we should have enough information in the ElaboratedType itself to perform instantiation and elaborated-type-specifier name lookup without having to dig deeper into the type that the ElaboratedType points to.

  - Doug

I guess I've misunderstood something...

Tell me where I'm wrong:

- non qualified enum X would be represented as ElaboratedType of
EnumType (exactly as now)

- qualified not dependent enum A::X would be represented as
ElaboratedType of EnumType using the (to be inserted) optional NNS

- qualified dependent enum A::X ???

Here I'm lost, from your previous message I believed you want to leave
it as now (ElaboratedType of TypenameType), but now I thought I've
misunderstood (as PR5681 is related to this case).

How you'd like to represent this case, exactly?

There is an option (C). Here's what I think each of the types
should do:

ElaboratedType: Should handle all elaborated-type-specifiers in
the C++0x sense, which means a type referenced via the
enum/class/struct/union keyword. This class should be extended
with an optional NestedNameSpecifier to handle qualified
elaborated-type-specifiers, ideally in a implementation-detail
base class so that we don't pay a size penalty for "struct X".
Getting this right should make fixing PR5681 trivial.

Why don't have ElaboratedType of QualifiedNameType instead of
optional NNS?

It seems to me it would be more congruent:

1) ElaboratedType of EnumType (for non qualified enum X) 2)
ElaboratedType of QualifiedNameType of EnumType (for qualified not
dependent enum A::X) 3) ElaboratedType of TypenameType (for
qualified dependent enum A::X)

The problem is that elaborated-type-specifiers in the C++ language
have very different name-lookup semantics than
QualifiedNameType/TypenameType (C++ [basic.lookup.elab]), so it's
cleaner to have the *Type nodes match up with language constructs.
That's actually what

http://llvm.org/bugs/show_bug.cgi?id=5681

is about: we're using ElaboratedType as an outer level of sugar (more
like what you're proposing than what I'm proposing), and because of
that we get name-lookup wrong when we instantiate the node that the
ElaboratedType points to. Instead, we should have enough information
in the ElaboratedType itself to perform instantiation and
elaborated-type-specifier name lookup without having to dig deeper
into the type that the ElaboratedType points to.

I guess I've misunderstood something...

Tell me where I'm wrong:

- non qualified enum X would be represented as ElaboratedType of
EnumType (exactly as now)

It's an ElaboratedType whose canonical type is an EnumType.

- qualified not dependent enum A::X would be represented as
ElaboratedType of EnumType using the (to be inserted) optional NNS

Yes, same thing (the canonical type is an EnumType).

- qualified dependent enum A::X ???

Here I'm lost, from your previous message I believed you want to leave
it as now (ElaboratedType of TypenameType), but now I thought I've
misunderstood (as PR5681 is related to this case).

I wanted this to be an ElaboratedType... but it's a completely different kind of elaborated type than the first two cases. Actually, I'm coming around to the idea of TypenameType morphing into some kind of DependentQualifiedNameType node, which covers typename/class/struct/enum. The lookup is different for those cases, but otherwise it gets the point across.

  - Doug

... and after talking with John for a bit, we're starting to like the following scheme:

DependentQualifiedNameType: captures all qualified types with dependent names, including elaborated-type-specifiers (class/struct/union/enum), typename-specifiers (typename), and implied dependent name types (X<T>::type as a base class).

ElaboratedType: captures all elaborated-type-specifiers (class/struct/union/enum), typename-specifiers (typename), and qualified names (no other adornment) where we were able to look up the type. These nodes can have an optional NestedNameSpecifier, and will always have either a class/struct/union/enum keyword or a nested-name-specifier.

  - Doug

Enea or Abramo, are either of you working on this? If not, I’ll go ahead and implement this change.

  • Doug

No, we had not yet begun to implement this, cause other urgent works.

Please proceed freely when you wish to.

Abramo Bagnara wrote:

[...]

Enea or Abramo, are either of you working on this? If not, I'll go ahead
and implement this change.

No, we had not yet begun to implement this, cause other urgent works.

Please proceed freely when you wish to.

As Abramo said, we are currently very busy with other stuff.
I reckon that my messages could let you believe that we were actively working on it ... sorry for that.

Cheers,
Enea.