Correctly diagnose taking the address of a register variable in C

We caught the cases where the user would explicitly use the & operator,
but we were missing implicit conversions such as array decay.

Fixes PR26336. Thanks to Samuel Neves for inspiration for the patch.
This commit is contained in:
Aaron Ballman 2021-07-22 14:49:21 -04:00
parent 4e0cefc640
commit 178c2b4c1e
3 changed files with 48 additions and 25 deletions

View File

@ -629,10 +629,10 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
if (ExprTy == TypeTy)
return E;
if (Kind == CK_ArrayToPointerDecay) {
// C++1z [conv.array]: The temporary materialization conversion is applied.
// We also use this to fuel C++ DR1213, which applies to C++11 onwards.
if (Kind == CK_ArrayToPointerDecay && getLangOpts().CPlusPlus &&
E->getValueKind() == VK_PRValue) {
if (getLangOpts().CPlusPlus && E->getValueKind() == VK_PRValue) {
// The temporary is an lvalue in C++98 and an xvalue otherwise.
ExprResult Materialized = CreateMaterializeTemporaryExpr(
E->getType(), E, !getLangOpts().CPlusPlus11);
@ -640,6 +640,26 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
return ExprError();
E = Materialized.get();
}
// C17 6.7.1p6 footnote 124: The implementation can treat any register
// declaration simply as an auto declaration. However, whether or not
// addressable storage is actually used, the address of any part of an
// object declared with storage-class specifier register cannot be
// computed, either explicitly(by use of the unary & operator as discussed
// in 6.5.3.2) or implicitly(by converting an array name to a pointer as
// discussed in 6.3.2.1).Thus, the only operator that can be applied to an
// array declared with storage-class specifier register is sizeof.
if (VK == VK_PRValue && !getLangOpts().CPlusPlus && !E->isPRValue()) {
if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
if (VD->getStorageClass() == SC_Register) {
Diag(E->getExprLoc(), diag::err_typecheck_address_of)
<< /*register variable*/ 3 << E->getSourceRange();
return ExprError();
}
}
}
}
}
if (ImplicitCastExpr *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {
if (ImpCast->getCastKind() == Kind && (!BasePath || BasePath->empty())) {

View File

@ -525,9 +525,13 @@ ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) {
// An lvalue or rvalue of type "array of N T" or "array of unknown bound of
// T" can be converted to an rvalue of type "pointer to T".
//
if (getLangOpts().C99 || getLangOpts().CPlusPlus || E->isLValue())
E = ImpCastExprToType(E, Context.getArrayDecayedType(Ty),
CK_ArrayToPointerDecay).get();
if (getLangOpts().C99 || getLangOpts().CPlusPlus || E->isLValue()) {
ExprResult Res = ImpCastExprToType(E, Context.getArrayDecayedType(Ty),
CK_ArrayToPointerDecay);
if (Res.isInvalid())
return ExprError();
E = Res.get();
}
}
return E;
}

View File

@ -45,24 +45,27 @@ void f0() {
int *_dummy1 = &(*(x1 + 1));
}
// FIXME: The checks for this function are broken; we should error
// on promoting a register array to a pointer! (C99 6.3.2.1p3)
void f1() {
register int x0[10];
int *_dummy00 = x0; // fixme-error {{address of register variable requested}}
int *_dummy01 = &(*x0); // fixme-error {{address of register variable requested}}
int *_dummy00 = x0; // expected-error {{address of register variable requested}}
int *_dummy01 = &(*x0); // expected-error {{address of register variable requested}}
register int x1[10];
int *_dummy1 = &(*(x1 + 1)); // fixme-error {{address of register variable requested}}
int *_dummy1 = &(*(x1 + 1)); // expected-error {{address of register variable requested}}
register int *x2;
int *_dummy2 = &(*(x2 + 1));
register int x3[10][10][10];
int (*_dummy3)[10] = &x3[0][0]; // expected-error {{address of register variable requested}}
int(*_dummy3)[10] = &x3[0][0]; // expected-error {{address of register variable requested}}
register struct { int f0[10]; } x4;
int *_dummy4 = &x4.f0[2]; // expected-error {{address of register variable requested}}
add_one(x0); // expected-error {{address of register variable requested}}
(void)sizeof(x0); // OK, not an array decay.
int *p = ((int *)x0)++; // expected-error {{address of register variable requested}}
}
void f2() {
@ -86,12 +89,8 @@ void f4() {
void f5() {
register int arr[2];
/* This is just here because if we happened to support this as an
lvalue we would need to give a warning. Note that gcc warns about
this as a register before it warns about it as an invalid
lvalue. */
int *_dummy0 = &(int*) arr; // expected-error {{cannot take the address of an rvalue}}
int *_dummy1 = &(arr + 1); // expected-error {{cannot take the address of an rvalue}}
int *_dummy0 = &(int*) arr; // expected-error {{address of register variable requested}}
int *_dummy1 = &(arr + 1); // expected-error {{address of register variable requested}}
}
void f6(register int x) {