aaron.ballman added a comment.

In D93630#2478977 <https://reviews.llvm.org/D93630#2478977>, @vsavchenko wrote:

> I guess I want to clarify one point here, after this patch the parser **will 
> not assume** that statement always follows statement attributes.  We simply 
> turn off the assumption that what follows is a declaration, parser will 
> simply determine whether it is a statement or a declaration, it can do it on 
> its own without attribute's "help".

We used to say that if there's a GNU attribute (or we're parsing a declaration 
statement), what follows must be a declaration. Now we're using the attributes 
in the given attribute list to decide whether what follows is a declaration or 
a statement (if all of the attributes are statement attributes, we don't treat 
what follows as being a declaration unless we're in a declaration statement). 
Or am I misreading the code?

e.g.,

  void foo(void) {
    __attribute__((fallthrough)) i = 12;
  }

Before patch:

  F:\llvm-project>llvm\out\build\x64-Debug\bin\clang.exe -fsyntax-only 
"F:\Aaron Ballman\Desktop\test.c"
  F:\Aaron Ballman\Desktop\test.c:2:32: warning: type specifier missing, 
defaults to 'int' [-Wimplicit-int]
    __attribute__((fallthrough)) i = 12;
                                 ^
  F:\Aaron Ballman\Desktop\test.c:2:18: error: 'fallthrough' attribute cannot 
be applied to a declaration
    __attribute__((fallthrough)) i = 12;
                   ^             ~
  1 warning and 1 error generated.

After patch:

  F:\llvm-project>llvm\out\build\x64-Debug\bin\clang.exe -fsyntax-only 
"F:\Aaron Ballman\Desktop\test.c"
  F:\Aaron Ballman\Desktop\test.c:2:32: error: use of undeclared identifier 'i'
    __attribute__((fallthrough)) i = 12;
                                 ^
  1 error generated.

So I think this will cause issues for the suppression attribute you're working 
on if we allow it to have a GNU spelling with valid C89 code like; 
`__attribute__((suppress)) i = 12;` or `__attribute__((suppress)) g() { return 
12; }` which may be intended to suppress the "type specifier missing" 
diagnostic (somewhat amusingly, since the user could also just add the `int` to 
the declaration since they're changing the code anyway).



================
Comment at: clang/lib/Parse/ParseStmt.cpp:213
              ParsedStmtContext()) &&
-        (GNUAttributeLoc.isValid() || isDeclarationStatement())) {
+        ((GNUAttributeLoc.isValid() && !Attrs.back().isStmtAttr()) ||
+         isDeclarationStatement())) {
----------------
vsavchenko wrote:
> aaron.ballman wrote:
> > vsavchenko wrote:
> > > aaron.ballman wrote:
> > > > I think you need to ensure there's at least one attribute before 
> > > > checking `!Attrs.back().isStmtAttr()` as this may cause problems if the 
> > > > user does something odd like `__attribute__(()) int x;` (I don't know 
> > > > if this will result in a valid `GNUAttributeLoc` with no attributes or 
> > > > not.)
> > > > 
> > > > I'm not certain that logic is perfect either. It would be pretty 
> > > > mysterious to handle these cases differently:
> > > > ```
> > > > __attribute__((stmt_attr, decl_attr)) int a, b, c;
> > > > __attribute__((decl_attr, stmt_attr)) int x, y, z;
> > > > ```
> > > Yep, my bad.  I changed it so that ALL the attributes in the front should 
> > > be statement attributes.
> > I think this is a better approach but it still doesn't leave us a clean way 
> > to handle attributes that are both declaration and statement attributes. 
> > I'm nervous about this in the same way I'm nervous about GNU attributes 
> > sliding around in general -- it doesn't always work out the way a user 
> > might expect.
> > 
> > `nomerge` is a good example. You currently cannot write this code:
> > ```
> > int f(void);
> > 
> > void func(void) {
> >   __attribute__((nomerge)) static int i = f();
> > }
> > ```
> > because of the way GNU attributes start a declaration. However, I think 
> > this could also be considered a bug because `nomerge` on a statement (which 
> > this also is) will ensure that the call expression to `f()` is not merged 
> > during optimization.
> > 
> > If we now say "GNU attribute can now be used at the start of an arbitrary 
> > statement", we have to late parse the attribute until we know what kind of 
> > statement we have. Because the user could have written:
> > ```
> > void func(void) {
> >   __attribute__((nomerge)) extern void f(void), g(int), h(void);
> > }
> > ```
> > which is currently accepted today and would change meaning if we treated 
> > `nomerge` as a statement attribute.
> > However, I think this could also be considered a bug because nomerge on a 
> > statement (which this also is) will ensure that the call expression to f() 
> > is not merged during optimization.
> 
> If we look at `clang/test/Sema/attr-nomerge.cpp`, we can see that we have 
> exactly the same behavior as in C++ with `[[clang::nomerge]]`:
> 
> ```
> [[clang::nomerge]] static int i = f(); // expected-error {{'nomerge' 
> attribute cannot be applied to a declaration}}
> ```
> 
> > If we now say "GNU attribute can now be used at the start of an arbitrary 
> > statement", we have to late parse the attribute until we know what kind of 
> > statement we have. Because the user could have written:
> >
> > ```void func(void) {
> >  __attribute__((nomerge)) extern void f(void), g(int), h(void);
> >}```
> >which is currently accepted today and would change meaning if we treated 
> >nomerge as a statement attribute.
> 
> I checked that as well and for this example before this patch, after this 
> patch, and in C++ when using `[[clang::nomerge]]` we get exactly the same 
> behavior: 3 errors `'nomerge' attribute cannot be applied to a declaration`.
> 
> 
> If we look at clang/test/Sema/attr-nomerge.cpp, we can see that we have 
> exactly the same behavior as in C++ with [[clang::nomerge]]

I don't read it quite the same way -- in C and C++, the syntactic position of 
the attribute defines what the attribute appertains to, unlike with GNU 
attributes. So the end result is the same, but for different reasons. In the 
case of the C or C++ attribute spelling, an attribute at that position applies 
to all of the declarations in the declaration group, and the declaration is of 
the wrong type (hence the error). In the case of the GNU attribute, we guess 
wrong whether the attribute appertains to the declaration(s) or the statement 
because we don't know which to pick solely based on the name of the attribute. 
We wind up picking the declaration version and it applies to the wrong kind of 
declaration so we get the error. However, it's defensible to argue that as part 
of the heuristic for determining what the attribute appertains to we should be 
checking that the attribute actually applies successfully -- e.g., we try it as 
a decl attr, find that it doesn't apply, then try again as a stmt attr. (Note, 
I'm not at all suggesting that we should *do* this work, just pointing out that 
the "sliding" behavior of GNU attributes makes this tricky to say what the 
right answer is.)

> I checked that as well and for this example before this patch, after this 
> patch, and in C++ when using [[clang::nomerge]] we get exactly the same 
> behavior: 3 errors 'nomerge' attribute cannot be applied to a declaration.

Huh, that's odd, because for me, before this patch, I get no errors with that 
example: https://godbolt.org/z/fsGr5n


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D93630

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to