[PATCH] D126061: [clang] [WIP] Reject non-declaration C++11 attributes on declarations

Martin Böhme via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Wed May 25 05:50:23 PDT 2022


mboehme marked 6 inline comments as done.
mboehme added a comment.

Thank you @rsmith and @aaron.ballman for the detailed review comments!

I hope I've addressed everything -- do let me know if there's anything I've missed.

In D126061#3528966 <https://reviews.llvm.org/D126061#3528966>, @rsmith wrote:

> This direction looks good to me. Thanks!
>
> Regarding the possibility of storing the declaration attributes on the `DeclSpec` rather than on the `Declarator`, I wonder if a better choice might be to make the `Declarator` hold a non-owning reference to the attributes, like it does for the `DeclSpec`?

Thanks for the suggestion -- this has turned out to make the code simpler and more logical in a number of places.



================
Comment at: clang/include/clang/Sema/DeclSpec.h:1853-1856
   /// Attrs - Attributes.
   ParsedAttributes Attrs;
 
+  ParsedAttributes DeclarationAttrs;
----------------
aaron.ballman wrote:
> We should probably rename `Attrs` to be less generic and add some comments to explain why there are two lists of parsed attributes.
I've added some comments to clarify the difference. I'm not sure what a good alternative name for `Attrs` would be though. The only obvious candidate that comes to mind is `DeclaratorAttrs`, but that seems pretty redundant given that this is a field of `Declarator`.

I think there is actually a good case for calling the first list simply `Attrs` because, unlike `DeclarationAttrs`, it applies directly to the `Declarator`. This distinction is amplified by the fact that, since your review, I've changed `DeclarationAttrs` to be a reference, not a by-value field.


================
Comment at: clang/include/clang/Sema/DeclSpec.h:1975-1984
   /// Reset the contents of this Declarator.
   void clear() {
+    clearExceptDeclarationAttrs();
+    DeclarationAttrs.clear();
+  }
+
+  /// Reset the contents of this Declarator, except for the declaration
----------------
rsmith wrote:
> Note that neither of these functions clear the `DeclSpec`. I suppose that makes sense given that the `Declarator` does not own the `DeclSpec` but merely holds a reference to it. Perhaps we could do the same thing with the declaration attributes?
This is a great idea that also helps with other things. It's similar in spirit to the idea that I'd floated in our discussion on https://reviews.llvm.org/D111548 of introducing a `Declaration` and having `Declarator` hold a reference to that, but it seemed a bit excessive to introduce a new `Declaration` class solely for the purpose of holding the declaration attributes. Holding a reference directly to the declaration attributes achieves the same thing, but without having to introduce yet another class. I like it.

One implication of this is that we should only have a const version of `getDeclarationAttributes()`; a non-const version would be dangerous because we're potentially sharing the attribute list with other declarators. (In fact, this wasn't really sound before either because we were effectively already sharing the attribute list too by moving it around between declarators.) Fortunately, it turns out that all of the places that were using the non-const `getDeclarationAttributes()` didn't actually need to be manipulating the declaration attribute list. I've added source code comments to call this out where appropriate.


================
Comment at: clang/include/clang/Sema/ParsedAttr.h:657-658
+  /// "slides" to the decl-specifier-seq).
+  /// Attributes with GNU, __declspec or keyword syntax generally slide
+  /// to the decl-specifier-seq. C++11 attributes specified ahead of the
+  /// declaration always appertain to the declaration according to the standard,
----------------
aaron.ballman wrote:
> rsmith wrote:
> > I don't think this sentence is correct: "Attributes with GNU, __declspec or keyword syntax generally slide to the decl-specifier-seq." Instead, I think those attribute syntaxes are never parsed as declaration attributes in the first place, so there is no possibility of "sliding" anywhere -- they simply always are decl-spec attributes.
> That's fair -- I tend to think "sliding" is also what happens (at least notionally) in a case like `__attribute__((address_space(1))) int *i;` because it's a type attribute rather than a declaration attribute, but that's also a declaration specifier, so it doesn't really slide anywhere.
Yes, this was inteded to refer to the fact that non-[[]] attributes also notionally slide to the `DeclSpec`. In the initial version of my patch, it was possible for `slidesFromDeclToDeclSpec()` to be called on non-[[]] attributes within the default case in `ProcessDeclAttribute()`.

However, I've realized that this is confusing; it's better to restrict this function only to [[]] attributes and to do an explicit check in `ProcessDeclAttribute()` to see whether we're looking at a [[]] attribute. I've added documentation to this function making it clear that it should only be called on [[]] attributes, and I've also added an assertion to enforce this.

All other places that call `slidesFromDeclToDeclSpec()` are already guaranteed to only call it on [[]] attributes because those attributes come from `Declarator::getDeclarationAttributes()`, and that is only ever populated with [[]] attributes. (I've added an assertion to make sure this is and continues to be true.)


================
Comment at: clang/lib/Parse/ParseDecl.cpp:1832
   // Parse the common declaration-specifiers piece.
   ParsingDeclSpec DS(*this);
 
----------------
rsmith wrote:
> Ideally I think we should pass the `DeclSpecAttrs` into `DS` up-front here. That'd keep the behavior closer to the behavior we get for attributes we parse in the `ParseDeclarationSpecifiers` call below, and will keep attributes in the correct order in the case of something like `__attribute__((a)) int __attribute__((b)) x;` at block scope.
Done. Unfortunately this did require adding the `OriginalDeclSpecAttrs` for the purpose of the `ProhibitAttributes()` call below. That call can't simply use `DS.getAttributes()` because `ParseDeclarationSpecifiers()` might have added attributes to that that are legitimately allowed to be there. For example, here's a case from test/SemaCXX/MicrosoftExtensions.cpp that would otherwise break:

```
  // expected-warning at +1 {{attribute 'deprecated' is ignored, place it after "struct" to apply attribute to type declaration}}
  struct D {} __declspec(deprecated);
```

Interestingly, I believe the attribute ordering was already correct before this change because `DeclSpec::takeAttributesFrom()` adds attributes at the beginning of the list. (It may even be that it does this to support this very use case.) However, as we discuss elsewhere, this behavior is potentially surprising, and we may want to change it at some point, so it's better not to rely on it in the first place.


================
Comment at: clang/lib/Parse/ParseDecl.cpp:2188
     // Parse the next declarator.
-    D.clear();
+    D.clearExceptDeclarationAttrs();
     D.setCommaLoc(CommaLoc);
----------------
aaron.ballman wrote:
> rsmith wrote:
> > I wonder if a name like `prepareForNextDeclarator` or `clearForComma` would be better here -- something that indicates why we're clearing rather than describing how.
> Personally, I like `prepareForNextDeclarator()` -- that's nice and clear (to me).
Now that `Declarator` only holds a reference to the declaration attributes, the additional `clearExceptDeclarationAttrs()` has become unnecessary. With that, I think the original `clear()` name is acceptable?


================
Comment at: clang/lib/Parse/ParseDecl.cpp:4323-4326
+    // C2x draft 6.7.2.1/9 : "The optional attribute specifier sequence in a
+    // member declaration appertains to each of the members declared by the
+    // member declarator list; it shall not appear if the optional member
+    // declarator list is omitted."
----------------
aaron.ballman wrote:
> Good catch! This also applies in C++: http://eel.is/c++draft/class#mem.general-14
> 
> I think you should add some test coverage for this, along the lines of:
> ```
> struct S {
>   [[clang::annotate("test")]] int; // The attribute should be diagnosed (as an error?)
> };
> ```
Good point. Test added in test/Parser/c2x-attributes.c.


================
Comment at: clang/lib/Parse/ParseDecl.cpp:4385
+    // can put them on the next field.
+    Attrs.takeAllFrom(DeclaratorInfo.D.getDeclarationAttributes());
   }
----------------
rsmith wrote:
> I think we'd benefit here from the `Declarator` only holding a reference to the declaration attributes rather than owning them.
Agree -- this has made this function much more straightforward.


================
Comment at: clang/lib/Parse/ParseDecl.cpp:6978-6997
     // Parse any C++11 attributes.
-    MaybeParseCXX11Attributes(DS.getAttributes());
+    ParsedAttributes ArgDeclAttrs(AttrFactory);
+    MaybeParseCXX11Attributes(ArgDeclAttrs);
 
-    // Skip any Microsoft attributes before a param.
-    MaybeParseMicrosoftAttributes(DS.getAttributes());
-
-    SourceLocation DSStart = Tok.getLocation();
+    ParsedAttributes ArgDeclSpecAttrs(AttrFactory);
 
     // If the caller parsed attributes for the first argument, add them now.
----------------
aaron.ballman wrote:
> rsmith wrote:
> > Seems to be mostly pre-existing, but I don't think this is right. The `FirstArgAttrs` are all decl-specifier attributes (they're parsed in `ParseParenDeclarator`, where we look for GNU attributes and `__declspec` attributes), so if we parsed any of those, we should not now parse any syntax that is supposed to precede decl-specifier attributes. The current code allows attributes to appear in the wrong order in the case where we need to disambiguate a paren declarator: https://godbolt.org/z/bzK6n8obM (note that the `g` case properly errors, but the `f` case that needs lookahead to determine whether the `(` is introducing a function declarator incorrectly accepts misplaced attributes).
> Good catch!
Thank you -- done!


================
Comment at: clang/lib/Parse/ParseDeclCXX.cpp:2716
     // Otherwise, it must be a using-declaration or an alias-declaration.
     return ParseUsingDeclaration(DeclaratorContext::Member, TemplateInfo,
+                                 UsingLoc, DeclEnd, DeclAttrs, AS);
----------------
rsmith wrote:
> Do we need to `ProhibitAttrs(DeclSpecAttrs)` along this code path? For example:
> ```
> struct A { int a; };
> struct B : A { [uuid("1234")] using A::a; };
> ```
> ... currently warns (for `-target x86_64-windows`) but I think with this patch we'll silently drop the `uuid` attribute on the floor.
Good point.

Instead of doing a `ProhibitAttributes()`, I've decided to simply move the `MaybeParseMicrosoftAttributes()` down below this point -- that seemed more straightforward.

I've added a test in test/Parser/MicrosoftExtensions.cpp.

Some considerations:

  - With this patch, we've "upgraded" the warning to a hard error. I think this is defensible though because b) the Microsoft compiler also flags this as a hard error (https://godbolt.org/z/jrx6Y1Y83), b) most people will be using Clang to compile code that they compiled with MSVC originally or in parallel, and c) it's an error that I think people are unlikely to make in the first place,

- The error message we get isn't great, but it's not really worse than the Microsoft compiler's error message, and as noted above, this is an error that I think people are unlikely to make.




================
Comment at: clang/lib/Parse/ParseObjc.cpp:661
         allTUVariables.push_back(
-            ParseDeclaration(DeclaratorContext::File, DeclEnd, attrs));
+            ParseDeclaration(DeclaratorContext::File, DeclEnd, attrs, attrs));
         continue;
----------------
rsmith wrote:
> It's a bit confusing to use the same `attrs` variable twice here. Would be clearer if either `attrs` were renamed to `NoAttrs` or if you used two different variables. And... actually, given that `ParseDeclaration` takes *mutable* references to its attribute lists, I'm pretty concerned by passing in two different mutable references to the same variable -- that seems error-prone.
> 
> But see comments elsewhere; I think we can remove the `DeclSpecAttrs` parameter from `ParseDeclaration` entirely.
Good points -- I've used two separate variables.


================
Comment at: clang/lib/Parse/ParseObjc.cpp:1233-1234
   // Now actually move the attributes over.
   takeDeclAttributes(attrs, D.getMutableDeclSpec().getAttributes());
+  takeDeclAttributes(attrs, D.getDeclarationAttributes());
   takeDeclAttributes(attrs, D.getAttributes());
----------------
aaron.ballman wrote:
> rsmith wrote:
> > I think we should keep the attributes in appearance order.
> +1
> 
> FWIW, we run into some "fun" bugs with attribute orders and declaration merging where the orders are opposite and it causes problems as in https://godbolt.org/z/58jTM4sGM (note how the error and the note swap positions), so the order of attributes tends to be important (both for semantic behavior as well as diagnostic behavior).
As it turns out, we can never pass declaration attributes in here anyway -- see the newly added assertion at the top of the function. This is a good thing, because I've eliminated the non-const version of `Declarator::getDeclarationAttributes()`.


================
Comment at: clang/lib/Parse/ParseStmt.cpp:198
+      ParsedAttributes Attrs(AttrFactory);
+      ConcatenateAttributes(CXX11Attrs, GNUAttrs, Attrs);
+
----------------
aaron.ballman wrote:
> I *think* this is going to be okay because attributes at the start of a statement have a very specific ordering, but one thing I was slightly worried about is attribute orders getting mixed up if the caller used something like `ParseAttributes()` or `MaybeParseAttributes()` where you can interleave the syntaxes.
> 
> If this turns out to be a real issue at some point, I suppose we could have `ConcatenateAttributes()` do insertions in sort order based on source locations, but I think it'd be best to avoid that unless we see a real issue.
Thanks for pointing this out. I agree we shouldn't do this sorting unless it turns out to be necessary.


================
Comment at: clang/lib/Parse/ParseStmt.cpp:242-246
+        Decl = ParseDeclaration(DeclaratorContext::Block, DeclEnd, CXX11Attrs,
+                                GNUAttrs, &GNUAttributeLoc);
       } else {
-        Decl = ParseDeclaration(DeclaratorContext::Block, DeclEnd, Attrs);
+        Decl = ParseDeclaration(DeclaratorContext::Block, DeclEnd, CXX11Attrs,
+                                GNUAttrs);
----------------
aaron.ballman wrote:
> rsmith wrote:
> > I think this is the only place where we're passing decl-specifier-seq attributes into `ParseDeclaration`. There are only two possible cases here:
> > 
> > 1) We have a simple-declaration, and can `ParseSimpleDeclaration` directly.
> > 2) We have a static-assert, using, or namespace alias declaration, which don't permit attributes at all.
> > 
> > So I think we *could* simplify this so that decl-spec attributes are never passed into `ParseDeclaration`:
> > 
> > * If the next token is `kw_static_assert`, `kw_using`, or `kw_namespace`, then prohibit attributes and call `ParseDeclaration`.
> > * Otherwise, call `ParseSimpleDeclaration` and pass in the attributes.
> > * Remove the `DeclSpecAttrs` list from `ParseDeclaration`.
> > 
> > I'm not requesting a change here -- I'm not sure whether that's a net improvement or not -- but it seems worth considering.
> I think this is a good avenue to explore -- passing in two different attribute lists means someone will at some point get it wrong by accident, so only having one attribute list reduces the chances for bugs later. I don't imagine static assertions or namespaces will get leading attributes. However...
> 
> I think asm-declaration and using-directive are also a bit special -- they're allowed to have leading attributes: http://eel.is/c++draft/dcl.dcl#nt:asm-declaration and http://eel.is/c++draft/dcl.dcl#nt:using-directive
> 
> Do we also have to handle opaque-enum-declaration here? http://eel.is/c++draft/dcl.dcl#nt:block-declaration
I investigated this, but I'm not sure it's a net win.

> * If the next token is `kw_static_assert`, `kw_using`, or `kw_namespace`, then prohibit attributes and call `ParseDeclaration`.

Or if the next token is `kw_inline` and the token after that is `kw_namespace`...

I think going down this path would mean duplicating all of the case distinctions in `ParseDeclaration()` -- and if we add more cases in the future, we'd have to make sure to replicate them here. I think overall it keeps the code simpler to accept the additional `DeclSpecAttrs` parameter.

> Do we also have to handle opaque-enum-declaration here? http://eel.is/c++draft/dcl.dcl#nt:block-declaration

A moot point, probably, but I believe this is handled by `ParseEnumSpecifier()`, which is called from `ParseDeclarationSpecifiers()`, so there would be no need to handle it separately.


================
Comment at: clang/lib/Parse/ParseStmt.cpp:317-318
   case tok::kw_asm: {
-    ProhibitAttributes(Attrs);
+    ProhibitAttributes(CXX11Attrs);
+    ProhibitAttributes(GNUAttrs);
     bool msAsm = false;
----------------
aaron.ballman wrote:
> It seems like we might have a pre-existing bug here for [[]] attributes? http://eel.is/c++draft/dcl.dcl#nt:asm-declaration
Yes, we do!

Fixed, and tests added in test/Parser/asm.c and test/Parser/asm.cpp.


================
Comment at: clang/lib/Parse/ParseStmt.cpp:329-330
   case tok::kw___if_not_exists:
-    ProhibitAttributes(Attrs);
+    ProhibitAttributes(CXX11Attrs);
+    ProhibitAttributes(GNUAttrs);
     ParseMicrosoftIfExistsStatement(Stmts);
----------------
aaron.ballman wrote:
> Seeing this pattern over and over again... I wonder if we want a variadic version of this function so we can call `ProhibitAttribtues(CXX11Attrs, GNUAttrs);` or if that's just overkill.
I would lean towards keeping it simple. Duplicating the `ProhibitAttributes()` is more verbose, but it's also less surprising.


================
Comment at: clang/lib/Parse/Parser.cpp:935-937
+      ParsedAttributes DeclSpecAttrs(AttrFactory);
+      return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
+                              DeclSpecAttrs);
----------------
rsmith wrote:
> For consistency with the below cases, call this one `EmptyDeclSpecAttrs` too
Good point, done!


================
Comment at: clang/lib/Parse/Parser.cpp:1164
       DS.getParsedSpecifiers() == DeclSpec::PQ_StorageClassSpecifier) {
-    Decl *TheDecl = ParseLinkage(DS, DeclaratorContext::File);
+    Decl *TheDecl = ParseLinkage(DS, DeclaratorContext::File, Attrs);
     return Actions.ConvertDeclToDeclGroup(TheDecl);
----------------
aaron.ballman wrote:
> rsmith wrote:
> > We should `ProhibitAttrs` here rather than passing them on.
> > ```
> > [[]] extern "C" void f();
> > ```
> > ... is invalid. (Per the grammar in https://eel.is/c++draft/dcl.dcl#dcl.pre-1 and https://eel.is/c++draft/dcl.dcl#dcl.link-2 an attribute-specifier-seq can't appear here.)
> +1, looks like we're missing test coverage for that case (but those diagnostics by GCC or MSVC... hoo boy!): https://godbolt.org/z/cTfPbK837
Fixed and added a test in test/Parser/cxx0x-attributes.cpp.


================
Comment at: clang/lib/Sema/ParsedAttr.cpp:219
+  if (!isStandardAttributeSyntax())
+    return true;
+
----------------
aaron.ballman wrote:
> rsmith wrote:
> > I think this case is unreachable, because only the standard `[[...]]` syntax attributes are parsed as declaration attributes.
> I think it's unreachable today, but there's a nonzero chance for it to become reachable in the near future as WG21 and WG14 continue to contemplate adding standardized type attributes, so it seems reasonably forward-thinking to have it.
As explained elsewhere, in the first iteration of the change, we used to actually call this with non-[[]] attributes.

I've changed this now, and there's an assertion that this will only ever be called with [[]] attributes.


================
Comment at: clang/lib/Sema/ParsedAttr.cpp:224
+  // property is consciously not defined as a flag in Attr.td because we don't
+  // want new attributes to specify it.
+  switch (getParsedKind()) {
----------------
aaron.ballman wrote:
> Thoughts on the additional comment? And should we start to issue that deprecation warning now (perhaps as a separate follow-up commit)?
> Thoughts on the additional comment?

Yes, good point, we should point this out explicitly.

I've worded the comment slightly differently: we can definitely deprecate the "sliding" behavior for attributes in the `clang::` namespace, as we own them, but there may be compatibility concerns for other attributes. This may mean that we can never reduce this list to nothing, but we should try.

> And should we start to issue that deprecation warning now (perhaps as a separate follow-up commit)?

We're already doing this (see the tests in test/SemaCXX/adress-space-placement.cpp), though again only for attributes in the `clang::` namespace, as we don't really have the authority to deprecate this usage for other attributes.

I think the other question here is whether, by default, this should be an error, not a warning, but this would then presumably require a command-line flag to downgrade the error to a warning if needed (I believe @rsmith raised this point on https://reviews.llvm.org/D111548). If we do decide to make this an error by default, I'd like to do this in a followup change if possible, as a) this change is already a strict improvement over the status quo; b) adding a command-line flag with associated tests would further increase the scope of this change, and c) this change is a blocker for https://reviews.llvm.org/D111548, which others on my team are waiting to be able to use.


================
Comment at: clang/lib/Sema/ParsedAttr.cpp:306-307
+                                  ParsedAttributes &Result) {
+  // Note that takeAllFrom() puts the attributes at the beginning of the list,
+  // so to obtain the correct ordering, we add `Second`, then `First`.
+  Result.takeAllFrom(Second);
----------------
aaron.ballman wrote:
> rsmith wrote:
> > Are you sure about this? I looked at the implementation of `takeAllFrom` and `takePool`, and it looks like it adds the new attributes to the end:
> > ```
> > void AttributePool::takePool(AttributePool &pool) {
> >   Attrs.insert(Attrs.end(), pool.Attrs.begin(), pool.Attrs.end());
> >   pool.Attrs.clear();
> > }
> > ```
> As mentioned earlier, we've got some preexisting ordering confusion somewhere in our attribute processing code, so I wouldn't be surprised if we're getting close to finding the root cause of that.
> Are you sure about this? I looked at the implementation of `takeAllFrom` and `takePool`, and it looks like it adds the new attributes to the end:
> ```
> void AttributePool::takePool(AttributePool &pool) {
>   Attrs.insert(Attrs.end(), pool.Attrs.begin(), pool.Attrs.end());
>   pool.Attrs.clear();
> }
> ```

This is the `AttributePool`, however, and IIUC that only deals with memory management for attributes; the ordering of attributes there should not have any semantic implications I believe.

What's actually relevant for the semantic ordering of attributes is the call to `addAll()` in `takeAllFrom()`:

```
  void takeAllFrom(ParsedAttributes &Other) {
    assert(&Other != this &&
           "ParsedAttributes can't take attributes from itself");
    addAll(Other.begin(), Other.end());
    Other.clearListOnly();
    pool.takeAllFrom(Other.pool);
  }
```

And `addAll()` inserts at the beginning of the list:

```
  void addAll(iterator B, iterator E) {
    AttrList.insert(AttrList.begin(), B.I, E.I);
  }
```

The behavior is definitely surprising though. I speculate that it _may_ have been introduced to make `ParseSimpleDeclaration()` do the right thing (see my comments there).

> As mentioned earlier, we've got some preexisting ordering confusion somewhere in our attribute processing code, so I wouldn't be surprised if we're getting close to finding the root cause of that.

It's possible that other callers of `takeAllFrom()` or `addAll()` are expecting the attributes to get added at the end and are doing the wrong thing because of that. This seems worth investigating further, but I'd prefer to defer that to a followup change.


================
Comment at: clang/lib/Sema/SemaDeclAttr.cpp:8361
+        break;
+      if (AL.slidesFromDeclToDeclSpec()) {
+        if (AL.isStandardAttributeSyntax() && AL.isClangScope()) {
----------------
aaron.ballman wrote:
> rsmith wrote:
> > Should we also be checking here that `D` is a declaration that the attribute could have been moved onto -- that is, that it's a `DeclaratorDecl`? Presumably we should error rather than only warning on
> > ```
> > namespace N {}
> > [[clang::noderef]] using namespace N;
> > ```
> We have `ParsedAttr::diagAppertainsToDecl()`, but that produces a diagnostic about the attribute not applying to the declaration which we wouldn't want. However, if we really wanted this, we could modify ClangAttrEmitter.cpp to produce a helper function for testing appertainment.
Good point. I've added a check that we're looking at either a `DeclaratorDecl` or a `TypeAliasDecl`. The latter is necessary because we have existing use cases where attributes can be placed on an alias declaration and is interpreted as appertaining to the type; see this example from test/Parser/cxx0x-attributes.cpp:

```
using V = int; // expected-note {{previous}}
using V [[gnu::vector_size(16)]] = int; // expected-error {{redefinition with different types}}
```

The various tests in test/SemaCXX/address-space-placement.cpp now reflect our rules for where the attribute exhibits the legacy "sliding" behavior (with a mere warning) and where it is prohibited outright (see the `expected-error` cases in that test).

> However, if we really wanted this, we could modify ClangAttrEmitter.cpp to produce a helper function for testing appertainment.

That's a good point. I think for the time being what we have here is sufficient however.


================
Comment at: clang/lib/Sema/SemaDeclAttr.cpp:8367-8368
+          // attributes might hurt portability.
+          S.Diag(AL.getLoc(), diag::warn_type_attribute_deprecated_on_decl)
+              << AL << D->getLocation();
+        }
----------------
aaron.ballman wrote:
> Do we have enough information at hand that we could produce a fix-it to move the attribute in question to the type position, or is that more work than it's worth?
I thought about this, but I think it would be non-trivial and would prefer to leave it to a future enhancement.


================
Comment at: clang/lib/Sema/SemaDeclAttr.cpp:9118
+      ProcessDeclAttributeOptions Options;
+      Options.IncludeCXX11Attributes = AL.isCXX11Attribute();
+      ProcessDeclAttribute(*this, nullptr, ASDecl, AL, Options);
----------------
aaron.ballman wrote:
> rsmith wrote:
> > aaron.ballman wrote:
> > > rsmith wrote:
> > > > This seems to be equivalent to setting `IncludeCXX11Attributes` to `true`, which seems to be equivalent to not setting it at all.
> > > Hmmm, not quite -- `AL.isCXX11Attribute()` may return `false` (like for the GNU spelling of this attribute).
> > It might, but we only care about the value of `IncludeCXX11Attributes` when processing a C++11 attribute, so it doesn't matter how this flag is set for a non-C++11 attribute.
> Oh, good observation, you're right!
Thanks for pointing this out -- done!


================
Comment at: clang/lib/Sema/SemaDeclAttr.cpp:9265
+        S, D, PD.getDeclSpec().getAttributes(),
+        ProcessDeclAttributeOptions().WithIgnoreTypeAttributes(true));
+  }
----------------
aaron.ballman wrote:
> rsmith wrote:
> > I think we should be skipping C++11 attributes here too: `int [[x]] a;` should not apply the attribute `x` to `a`, even if it's a declaration attribute.
> +1 to that example being something we want to diagnose.
> I think we should be skipping C++11 attributes here too: int [[x]] a; should not apply the attribute x to a, even if it's a declaration attribute.

Good point -- done.

I don't think this will ultimately make much of a difference because we shouldn't be putting C++11 declaration attributes on the `DeclSpec` anyway; there's code in `ParseDeclarationSpecifiers()` that prohibits that. (Prior to this change, we were prohibiting C++11 attributes on the `DeclSpec` outright; now, we only forbid non-type attributes, but that does mean we should never see declaration attributes here.) There are existing tests for this, for example in test/Parser/cxx0x-attributes.cpp (search for "attribute cannot be applied to a declaration).

However, it does seem more logical and consistent to exclude C++11 attributes here, so I've made this change.

One effect that this does have is the treatment of unknown C++11 attributes: Because we're excluding them now, we no longer diagnose them here; instead, I've modified `processTypeAttrs()` to diagnose unknown C++11 attributes on the `DeclSpec` there. Again, this seems the more logical way to do things: The attributes appertain to the type, so they should be diagnosed as part of processing type attributes.


================
Comment at: clang/lib/Sema/SemaType.cpp:201
+    // Set to indicate that, if we're currently processing the DeclSpec, the
+    // attributes we're seeing were actually written ahead of the declaration.
+    bool isProcessingDeclarationAttrs = false;
----------------
rsmith wrote:
> It's not immediately clear to me which side is "ahead of"; "before" or "after" would be clearer.
No longer applicable.

I've reverted the changes I made to `TypeProcessingState` that this comment applied to. I had originally modified `TypeProcessingState::getCurrentAttributes()` so that it would return the declaration attributes if we were currently processing those, but it turned out that this was not relevant to any of the use cases that `TypeProcessingState::getCurrentAttributes()` is used for.


================
Comment at: clang/lib/Sema/SemaType.cpp:593
   state.addIgnoredTypeAttr(attr);
-}
+ }
 
----------------
rsmith wrote:
> Phabricator shows this line as slightly over-indented, but that might just be phabricator being weird?
This was actually a mis-formatting that clang-format didn't complain about or fix. Fixed.


================
Comment at: clang/lib/Sema/SemaType.cpp:1826
+        // attributes might hurt portability.
+        if (AL.isStandardAttributeSyntax() && AL.isClangScope()) {
+          S.Diag(AL.getLoc(), diag::warn_type_attribute_deprecated_on_decl)
----------------
aaron.ballman wrote:
> Same question here as above on whether we can produce a fix-it here.
Again, I would prefer to exclude this from the scope of this change.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D126061/new/

https://reviews.llvm.org/D126061



More information about the cfe-commits mailing list