Skip to content

Commit 178c2b4

Browse files
committed
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.
1 parent 4e0cefc commit 178c2b4

File tree

3 files changed

+48
-25
lines changed

3 files changed

+48
-25
lines changed

clang/lib/Sema/Sema.cpp

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -629,16 +629,36 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
629629
if (ExprTy == TypeTy)
630630
return E;
631631

632-
// C++1z [conv.array]: The temporary materialization conversion is applied.
633-
// We also use this to fuel C++ DR1213, which applies to C++11 onwards.
634-
if (Kind == CK_ArrayToPointerDecay && getLangOpts().CPlusPlus &&
635-
E->getValueKind() == VK_PRValue) {
636-
// The temporary is an lvalue in C++98 and an xvalue otherwise.
637-
ExprResult Materialized = CreateMaterializeTemporaryExpr(
638-
E->getType(), E, !getLangOpts().CPlusPlus11);
639-
if (Materialized.isInvalid())
640-
return ExprError();
641-
E = Materialized.get();
632+
if (Kind == CK_ArrayToPointerDecay) {
633+
// C++1z [conv.array]: The temporary materialization conversion is applied.
634+
// We also use this to fuel C++ DR1213, which applies to C++11 onwards.
635+
if (getLangOpts().CPlusPlus && E->getValueKind() == VK_PRValue) {
636+
// The temporary is an lvalue in C++98 and an xvalue otherwise.
637+
ExprResult Materialized = CreateMaterializeTemporaryExpr(
638+
E->getType(), E, !getLangOpts().CPlusPlus11);
639+
if (Materialized.isInvalid())
640+
return ExprError();
641+
E = Materialized.get();
642+
}
643+
// C17 6.7.1p6 footnote 124: The implementation can treat any register
644+
// declaration simply as an auto declaration. However, whether or not
645+
// addressable storage is actually used, the address of any part of an
646+
// object declared with storage-class specifier register cannot be
647+
// computed, either explicitly(by use of the unary & operator as discussed
648+
// in 6.5.3.2) or implicitly(by converting an array name to a pointer as
649+
// discussed in 6.3.2.1).Thus, the only operator that can be applied to an
650+
// array declared with storage-class specifier register is sizeof.
651+
if (VK == VK_PRValue && !getLangOpts().CPlusPlus && !E->isPRValue()) {
652+
if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) {
653+
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
654+
if (VD->getStorageClass() == SC_Register) {
655+
Diag(E->getExprLoc(), diag::err_typecheck_address_of)
656+
<< /*register variable*/ 3 << E->getSourceRange();
657+
return ExprError();
658+
}
659+
}
660+
}
661+
}
642662
}
643663

644664
if (ImplicitCastExpr *ImpCast = dyn_cast<ImplicitCastExpr>(E)) {

clang/lib/Sema/SemaExpr.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -525,9 +525,13 @@ ExprResult Sema::DefaultFunctionArrayConversion(Expr *E, bool Diagnose) {
525525
// An lvalue or rvalue of type "array of N T" or "array of unknown bound of
526526
// T" can be converted to an rvalue of type "pointer to T".
527527
//
528-
if (getLangOpts().C99 || getLangOpts().CPlusPlus || E->isLValue())
529-
E = ImpCastExprToType(E, Context.getArrayDecayedType(Ty),
530-
CK_ArrayToPointerDecay).get();
528+
if (getLangOpts().C99 || getLangOpts().CPlusPlus || E->isLValue()) {
529+
ExprResult Res = ImpCastExprToType(E, Context.getArrayDecayedType(Ty),
530+
CK_ArrayToPointerDecay);
531+
if (Res.isInvalid())
532+
return ExprError();
533+
E = Res.get();
534+
}
531535
}
532536
return E;
533537
}

clang/test/Sema/expr-address-of.c

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,27 @@ void f0() {
4545
int *_dummy1 = &(*(x1 + 1));
4646
}
4747

48-
// FIXME: The checks for this function are broken; we should error
49-
// on promoting a register array to a pointer! (C99 6.3.2.1p3)
5048
void f1() {
5149
register int x0[10];
52-
int *_dummy00 = x0; // fixme-error {{address of register variable requested}}
53-
int *_dummy01 = &(*x0); // fixme-error {{address of register variable requested}}
50+
int *_dummy00 = x0; // expected-error {{address of register variable requested}}
51+
int *_dummy01 = &(*x0); // expected-error {{address of register variable requested}}
5452

5553
register int x1[10];
56-
int *_dummy1 = &(*(x1 + 1)); // fixme-error {{address of register variable requested}}
54+
int *_dummy1 = &(*(x1 + 1)); // expected-error {{address of register variable requested}}
5755

5856
register int *x2;
5957
int *_dummy2 = &(*(x2 + 1));
6058

6159
register int x3[10][10][10];
62-
int (*_dummy3)[10] = &x3[0][0]; // expected-error {{address of register variable requested}}
60+
int(*_dummy3)[10] = &x3[0][0]; // expected-error {{address of register variable requested}}
6361

6462
register struct { int f0[10]; } x4;
6563
int *_dummy4 = &x4.f0[2]; // expected-error {{address of register variable requested}}
64+
65+
add_one(x0); // expected-error {{address of register variable requested}}
66+
(void)sizeof(x0); // OK, not an array decay.
67+
68+
int *p = ((int *)x0)++; // expected-error {{address of register variable requested}}
6669
}
6770

6871
void f2() {
@@ -86,12 +89,8 @@ void f4() {
8689
void f5() {
8790
register int arr[2];
8891

89-
/* This is just here because if we happened to support this as an
90-
lvalue we would need to give a warning. Note that gcc warns about
91-
this as a register before it warns about it as an invalid
92-
lvalue. */
93-
int *_dummy0 = &(int*) arr; // expected-error {{cannot take the address of an rvalue}}
94-
int *_dummy1 = &(arr + 1); // expected-error {{cannot take the address of an rvalue}}
92+
int *_dummy0 = &(int*) arr; // expected-error {{address of register variable requested}}
93+
int *_dummy1 = &(arr + 1); // expected-error {{address of register variable requested}}
9594
}
9695

9796
void f6(register int x) {

0 commit comments

Comments
 (0)