On Nov 14, 2011, at 8:10 AM, David Blaikie wrote:
>> Some comments follow…
>
> Thanks for the feedback.
>
>> Index: include/clang/Basic/TokenKinds.def
>> ===================================================================
>> --- include/clang/Basic/TokenKinds.def (revision 143902)
>> +++ include/clang/Basic/TokenKinds.def (working copy)
>> @@ -559,6 +559,8 @@
>> // function template specialization (not a type),
>> // e.g., "std::swap<int>"
>> ANNOTATION(primary_expr) // annotation for a primary expression
>> +ANNOTATION(decltype) // annotation for a decltype expression,
>> + // e.g., "decltype(foo.bar())"
>>
>> // Annotation for #pragma unused(...)
>> // For each argument inside the parentheses the pragma handler will produce
>>
>> Did you consider using annot_typename for this? I ask because annot_typename
>> handles general types (including decltype), and is already well-supported
>> throughout the parser. It looks like you've checked for annot_decltype in
>> several places, but others (such as the tentative parser) would be confused
>> by this annotation token.
>
> No, I hadn't considered that - but at first blush I'd be concerned
> that this might end up being /too/ permissive, but I'll have a go
> mocking it up that way, it'll probably be tidier but I won't be sure
> what other scenarios might be accidentally accepted.
I'm fine with keeping annot_decltype. It's super-specific, so just make sure
you're auditing the various places where we have to deal with, e.g., the
'decltype' keyword or the annot_typename token so you catch all of the places
'decltype' can show up.
>> Index: include/clang/Basic/DiagnosticSemaKinds.td
>> ===================================================================
>> --- include/clang/Basic/DiagnosticSemaKinds.td (revision 143902)
>> +++ include/clang/Basic/DiagnosticSemaKinds.td (working copy)
>> @@ -3957,6 +3957,7 @@
>> "conversion function from %0 to %1 invokes a deleted function">;
>>
>> def err_expected_class_or_namespace : Error<"expected a class or
>> namespace">;
>> +def err_expected_class : Error<"expected a decltype of a class">;
>> def err_missing_qualified_for_redecl : Error<
>> "must qualify the name %0 to declare %q1 in this scope">;
>> def err_invalid_declarator_scope : Error<
>>
>> Could you provide a better error message here? Something that, at the very
>> least, tells the user what the actual type is?
>> Also, enum types would also be fine in C++11, so the diagnostic isn't quite
>> accurate.
>
> Sure - I was going off the (admittedly rather vague) existing message
> above. I've rephrased this to, for example:
>
> 'decltype(int())' (aka 'int') is not a namespace, class, or scoped
> enumeration
>
> I think it'd be helpful to use this diagnostic for the existing case
> above. It comes up most easily if you have a typedef that resolves to
> int for example:
>
> namespace foo { typedef int bar; }
>
> foo::bar::baz i; // "expected class or namespace" (^ points to the
> 'b' in 'bar')
>
> & you don't get told what foo::bar is. Though my version is a bit more
> verbose.
It's definitely an improvement. We only want that 'or scoped enumeration' bit
if we're in C++11.
I wonder if there's a phrase that could describe these kinds of entities with a
scope without having to enumerate the examples? Nothing comes to mind yet.
>> As an aside, I wonder if GCC supports typeof(blah)::foo, since typeof is the
>> predecessor to decltype.
>
> GCC 4.6 doesn't seem to support decltype or typeof in nested name
> specifiers. I don't have 4.7 on hand to compare it.
Okay! Let's just stick with decltype only.
>> Index: lib/Parse/ParseDeclCXX.cpp
>> ===================================================================
>> --- lib/Parse/ParseDeclCXX.cpp (revision 143902)
>> +++ lib/Parse/ParseDeclCXX.cpp (working copy)
>> @@ -633,14 +633,38 @@
>> /// 'decltype' ( expression )
>> ///
>> void Parser::ParseDecltypeSpecifier(DeclSpec &DS) {
>> + if (Tok.is(tok::kw_decltype)) {
>> + AnnotateDecltypeSpecifier();
>> + }
>> + assert(Tok.is(tok::annot_decltype) && "Not a decltype specifier");
>> + ExprResult Result = getExprAnnotation(Tok);
>> + if (Result.isInvalid()) {
>> + ConsumeToken();
>> + DS.SetTypeSpecError();
>> + return;
>> + }
>> + SourceLocation DecltypeLoc = ConsumeToken();
>> +
>> + const char *PrevSpec = 0;
>> + unsigned DiagID;
>> + if (DS.SetTypeSpecType(DeclSpec::TST_decltype, DecltypeLoc, PrevSpec,
>> + DiagID, Result.get())) {
>> + Diag(DecltypeLoc, DiagID) << PrevSpec;
>> + DS.SetTypeSpecError();
>> + }
>> +}
>>
>> I'm not a big fan of this flow. It means that we go through the
>> token-annotation logic even in the case where we're simply going to parse
>> the decltype. Please factor this so that straight parsing of decltype() is
>> simple, and we only annotate if we can't use the generated DeclSpec directly.
>
> I was modeling this off the behavior I saw in
> Parser::ParseDeclarationSpecifiers which always annotates the
> CXXScopeToken rather than using it directly, presumably (or so I
> thought) out of convenience of merging the two cases (where it was
> called without an annotation token or with one), though it did feel a
> bit questionable.
>
> I've switched this back to the original implementation (though still
> with better error recovery using SetTypeSpecError) & only annotating
> when necessary.
Thanks.
>> +void Parser::AnnotateDecltypeSpecifier() {
>> assert(Tok.is(tok::kw_decltype) && "Not a decltype specifier");
>>
>> + // consume 'decltype'
>> SourceLocation StartLoc = ConsumeToken();
>> - BalancedDelimiterTracker T(*this, tok::l_paren);
>> - if (T.expectAndConsume(diag::err_expected_lparen_after,
>> - "decltype", tok::r_paren)) {
>> - return;
>> - }
>> + if (Tok.isNot(tok::l_paren))
>> + Diag(Tok, diag::err_expected_lparen_after)
>> + << "decltype"
>> + << FixItHint::CreateInsertion(Tok.getLocation(), "(");
>> + else
>> + ConsumeParen();
>>
>> // Parse the expression
>>
>> Why aren't you using the BalancedDelimiterTracker here? It provides better
>> error recovery and checking than your direct checks for l_paren/r_paren.
>
> Originally I'd pulled this out/removed BalancedDelimiterTracker so
> that I could avoid consuming the trailing ')' & just mutate that into
> the annotation token I required. But since I ended up having to
> account for the case where I needed to insert a token to be the
> annotation token anyway (when recovering from a lack of ')') I assume
> I might as well just do that in all cases & keep with the old
> implementation.
>
> One problem I have using the BalancedDelimiterTracker is getting the
> last token it consumes so I can set the annotation token's end
> correctly. Though I think the only case where the
> BlancedDelimiterTracker doesn't set and end location is when it
> doesn't find one & goes all the way to eof. I haven't thought too much
> about what that situation means/how it comes out in my current code or
> how it should come out.
>
> [As for error recovery, BalancedDelimiterTracker does do the extra
> legwork to handle nesting quantities which is certainly valuable, but
> its error recovery is a bit different (& without a FixIt). It skips
> everything up to the next matching token, my error recovery was to
> insert the missing token - I wonder which is more likely to be
> correct? (it'd also be kind of nice to be able to find the location of
> the previous token & insert the fixit for the missing paren after the
> previous token rather than before the next token. But that's a minor
> whitespace improvement ("decltype(x y;" -> "decltype(x) y;" rather
> than "decltype(x )y;").]
Should BalancedDelimiterTracker be improved to help handle this case?
>> A couple notes on testing:
>>
>> - It would be good to make sure that decltype(T)::blah does the right thing
>> during template instantiation
>
> Good call - this didn't work immediately for dependent names. I've
> added a simple test case & fixed the bug I had blocking this scenario.
> (lib/Sema/SemaCXXScopeSpec.cpp:688 - was failing if the expression was
> not a TagType. I've made it more permissive to allow for any dependent
> type or non-dependent tag types)
Okay.
>> - I don't see any tests where decltype shows up some place where we have to
>> use tentative parsing (for the expression-or-declaration ambiguity)
>
> I've added a test case for this & made a small change to fix the
> scenario - but this change has somehow regressed a name mangling test
> related to templates and decltype usage... I haven't had time to
> investigate this in great detail but thought I'd show it as-is in case
> you or anyone else has some ideas about what's going on here.
I don't know off-hand what could have broken.
> Also, it seems currently that we don't support decltype in expressions
> (looks like it's half done - Parser::isCXXSimpleTypeSpecifier has
> decltype, but ParseCXXSimpleTypeSpecifier has a FIXME to add it). If I
> switch to using a typename annotation this might come out basically
> for free, perhaps.
Oops! Should be an easy fix.
> I haven't prototyped an implementation using typename yet, but if I
> get a chance (and/or if I fix the name mangling issue) I'll send an
> update.
Okay! I think the annot_typename idea was a bad one on my part; annot_decltype
seems to be the way to go.
I noticed that there's another (completely separable and not necessarily your
problem) decltype-related issue, which is that we don't parse:
template<typename T>
void f(T *ptr) {
ptr->~decltype(*ptr);
}
at all. Should be trivial following your changes :)
- Doug
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits