-
Notifications
You must be signed in to change notification settings - Fork 13.5k
Accessing qualified member of a structure or union rvalue allows creating qualified rvalues #96713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
@llvm/issue-subscribers-clang-frontend Author: None (Halalaluyafail3)
The following code demonstrates the issue:
```c
struct{
const int c;
}foo();
puts(_Generic(foo().c,int:"A",const int:"B"));
```
`foo` returns a structure that contains a qualified member named `c` of type `const int`. Calling `foo` results in an rvalue structure, which when accessing one of the members via `.` results in an rvalue. Clang does not remove qualifiers or _Atomic (C23 removes _Atomic from rvalues as well, and GCC does this in earlier versions as well) here while GCC does and the result of DR 423 seems intended to remove cases of qualified rvalues. Additionally, Clang actually generates a warning here with the default warnings stating that the `const int` branch will never be taken (and then it takes it anyway). The wording doesn't state that the result is the non-atomic (C23) and unqualified member when getting the member though I assume that is a defect.
|
The
So the member named here is not an lvalue because the returned structure object is not an lvalue. However, "if the first expression has qualified type" can be read to apply here and Clang's behavior is correct. I took a look at what other C compilers do and the results are wonderful: Clang, Chibicc, EDG: do not strip the qualifier So I'm going to ask on the WG14 reflectors to see what folks think. |
I lied, I didn't need to go ask WG14. _Generic's controlling operand behaves as though lvalue conversion happens and that needs to drop the qualifiers anyway. |
I lied again and have asked on WG14 because I think this is a standards wording defect or I'm reading the standard wrong. lvalue conversion only applies to lvalues, so if we do get an rvalue from the member access expression, then the lvalue conversion wouldn't drop qualifiers because no conversion occurs. https://godbolt.org/z/W64hxa4jj shows the compiler divergence for this. |
Interestingly, it seems that all compilers agree that with an array member qualifiers should not be removed in this situation (after decay it is no longer a qualified rvalue, but it being a qualified rvalue can still be observed with typeof). Also, I have discovered that GCC does not remove qualifiers from the result of |
Committee discussion on the WG14 reflectors suggests a few things: 1) qualified rvalues are a thing in C and that was a benign thing largely because qualifiers were never observable until |
It is possible to get a qualified rvalue in C. Consider: struct { cont int i; } foo(); _Generic(foo().i, ...); foo() returns an rvalue for the anonymous structure, the member access expression then results in an rvalue of type const int. The lvalue to rvalue conversion we perform on the controlling expression of the generic selection expression then fails to strip any qualifiers because the expression is already an rvalue. Discussion on the WG14 reflectors suggested that the qualifiers should still be stripped from the type of the controlling expression; the standard should be corrected to make this more clear. Fixes llvm#96713
If typeof can be used to observe the qualifiers and qualified rvalues exist, then that creates situations like the following: struct{
const int c;
}foo();
typeof(+foo().c)x;
x=1; Here I see three possible results:
|
I don't think that program violates a constraint. C23 6.2.5p31:
I think Clang's handling of that is correct because the function returns an rvalue, the A slightly different example would be:
where I believe you would generally get
And... Clang seems to be doing exactly that: https://godbolt.org/z/ndsjo9E7Y |
The term 'type category' is defined as such:
Section 6.2.5 "Types" Paragraph 30 N3220 From how I read this, the type category of
I assume you mean that the struct{
const int c;
}foo();
typeof(foo().c+foo().c)x;
x=1; The types of the operands to the
Section 6.3.1.8 "Usual arithmetic conversions" Paragraph 1 N3220 Using this, the result of the addition should be |
Can you imagine an interpretation where
Yes, sorry for the confusion!
I get the same interpretation as you do. IMO, this topic requires a WG14 paper to tease out what we want the standard to say. The author of the |
I think a good solution would be to convert to the type after lvalue conversions, if the expression was used in a context where lvalue conversions would apply had the expression been an lvalue instead. Similar to your proposed solution for this with _Generic but more generally. This would still allow struct{
_Atomic int c;
}foo(),bar;
+bar.c;//OK, lvalue conversions remove _Atomic
+foo().c;//Clang rejects Clang rejects this because |
The following code demonstrates the issue:
foo
returns a structure that contains a qualified member namedc
of typeconst int
. Callingfoo
results in an rvalue structure, which when accessing one of the members via.
results in an rvalue. Clang does not remove qualifiers or _Atomic (C23 removes _Atomic from rvalues as well, and GCC does this in earlier versions as well) here while GCC does and the result of DR 423 seems intended to remove cases of qualified rvalues. Additionally, Clang actually generates a warning here with the default warnings stating that theconst int
branch will never be taken (and then it takes it anyway). The wording doesn't state that the result is the non-atomic (C23) and unqualified member when getting the member though I assume that is a defect.The text was updated successfully, but these errors were encountered: