forked from OSchip/llvm-project
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:
parent
4e0cefc640
commit
178c2b4c1e
|
@ -629,16 +629,36 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
|
|||
if (ExprTy == TypeTy)
|
||||
return E;
|
||||
|
||||
// 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) {
|
||||
// The temporary is an lvalue in C++98 and an xvalue otherwise.
|
||||
ExprResult Materialized = CreateMaterializeTemporaryExpr(
|
||||
E->getType(), E, !getLangOpts().CPlusPlus11);
|
||||
if (Materialized.isInvalid())
|
||||
return ExprError();
|
||||
E = Materialized.get();
|
||||
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 (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);
|
||||
if (Materialized.isInvalid())
|
||||
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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue