[C11] Correct the resulting type for an assignment expression

In C, assignment expressions result in an rvalue whose type is the type
of the lhs of the assignment after it undergoes lvalue to rvalue
conversion. lvalue to rvalue conversion in C strips all qualifiers
including _Atomic.

We used getUnqualifiedType() which does not strip the _Atomic qualifier
when we should have used getAtomicUnqualifiedType(). This corrects the
usage and adds some comments to getUnqualifiedType() to make it more
clear that it does not strip _Atomic and that's on purpose (see C11
6.2.5p27).

This addresses Issue 48742.
This commit is contained in:
Aaron Ballman 2022-03-26 08:01:47 -04:00
parent c3b672a34c
commit bfa2f25d35
4 changed files with 28 additions and 8 deletions

View File

@ -82,7 +82,9 @@ Bug Fixes
alias, target) identifier instead of only processing one such ``#pragma weak``
per identifier.
Fixes `Issue 28985 <https://github.com/llvm/llvm-project/issues/28985>`_.
- Assignment expressions in C11 and later mode now properly strip the _Atomic
qualifier when determining the type of the assignment expression. Fixes
`Issue 48742 <https://github.com/llvm/llvm-project/issues/48742>`_.
- Unevaluated lambdas in dependant contexts no longer result in clang crashing.
This fixes Issues `50376 <https://github.com/llvm/llvm-project/issues/50376>`_,
`51414 <https://github.com/llvm/llvm-project/issues/51414>`_,

View File

@ -931,6 +931,10 @@ public:
/// The resulting type might still be qualified if it's sugar for an array
/// type. To strip qualifiers even from within a sugared array type, use
/// ASTContext::getUnqualifiedArrayType.
///
/// Note: In C, the _Atomic qualifier is special (see C2x 6.2.5p29 for
/// details), and it is not stripped by this function. Use
/// getAtomicUnqualifiedType() to strip qualifiers including _Atomic.
inline QualType getUnqualifiedType() const;
/// Retrieve the unqualified variant of the given type, removing as little

View File

@ -13637,15 +13637,15 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS,
}
}
// C99 6.5.16p3: The type of an assignment expression is the type of the
// left operand unless the left operand has qualified type, in which case
// it is the unqualified version of the type of the left operand.
// C99 6.5.16.1p2: In simple assignment, the value of the right operand
// is converted to the type of the assignment expression (above).
// C11 6.5.16p3: The type of an assignment expression is the type of the
// left operand would have after lvalue conversion.
// C11 6.3.2.1p2: ...this is called lvalue conversion. If the lvalue has
// qualified type, the value has the unqualified version of the type of the
// lvalue; additionally, if the lvalue has atomic type, the value has the
// non-atomic version of the type of the lvalue.
// C++ 5.17p1: the type of the assignment expression is that of its left
// operand.
return (getLangOpts().CPlusPlus
? LHSType : LHSType.getUnqualifiedType());
return getLangOpts().CPlusPlus ? LHSType : LHSType.getAtomicUnqualifiedType();
}
// Only ignore explicit casts to void.

View File

@ -61,3 +61,17 @@ int func_13 (int x, unsigned y) {
int func_14 (void) {
return data1 == 0;
}
void func_15(void) {
// Ensure that the result of an assignment expression properly strips the
// _Atomic qualifier; Issue 48742.
_Atomic int x;
int y = (x = 2);
int z = (int)(x = 2);
y = (x = 2);
z = (int)(x = 2);
y = (x += 2);
_Static_assert(__builtin_types_compatible_p(__typeof__(x = 2), int), "incorrect");
_Static_assert(__builtin_types_compatible_p(__typeof__(x += 2), int), "incorrect");
}