Skip to content

Commit

Permalink
Grab-bag of bit-field fixes:
Browse files Browse the repository at this point in the history
  - References to ObjC bit-field ivars are bit-field lvalues;
    fixes rdar://13794269, which got me started down this.
  - Introduce Expr::refersToBitField, switch a couple users to
    it where semantically important, and comment the difference
    between this and the existing API.
  - Discourage Expr::getBitField by making it a bit longer and
    less general-sounding.
  - Lock down on const_casts of bit-field gl-values until we
    hear back from the committee as to whether they're allowed.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181252 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
rjmccall committed May 6, 2013
1 parent 009735d commit 993f43f
Show file tree
Hide file tree
Showing 15 changed files with 147 additions and 24 deletions.
20 changes: 16 additions & 4 deletions include/clang/AST/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,12 +427,24 @@ class Expr : public Stmt {

public:

/// \brief Returns true if this expression is a gl-value that
/// potentially refers to a bit-field.
///
/// In C++, whether a gl-value refers to a bitfield is essentially
/// an aspect of the value-kind type system.
bool refersToBitField() const { return getObjectKind() == OK_BitField; }

/// \brief If this expression refers to a bit-field, retrieve the
/// declaration of that bit-field.
FieldDecl *getBitField();

const FieldDecl *getBitField() const {
return const_cast<Expr*>(this)->getBitField();
///
/// Note that this returns a non-null pointer in subtly different
/// places than refersToBitField returns true. In particular, this can
/// return a non-null pointer even for r-values loaded from
/// bit-fields, but it will return null for a conditional bit-field.
FieldDecl *getSourceBitField();

const FieldDecl *getSourceBitField() const {
return const_cast<Expr*>(this)->getSourceBitField();
}

/// \brief If this expression is an l-value for an Objective C
Expand Down
3 changes: 2 additions & 1 deletion include/clang/AST/ExprObjC.h
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,8 @@ class ObjCIvarRefExpr : public Expr {
SourceLocation l, SourceLocation oploc,
Expr *base,
bool arrow = false, bool freeIvar = false) :
Expr(ObjCIvarRefExprClass, t, VK_LValue, OK_Ordinary,
Expr(ObjCIvarRefExprClass, t, VK_LValue,
d->isBitField() ? OK_BitField : OK_Ordinary,
/*TypeDependent=*/false, base->isValueDependent(),
base->isInstantiationDependent(),
base->containsUnexpandedParameterPack()),
Expand Down
6 changes: 5 additions & 1 deletion include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1307,7 +1307,8 @@ def err_member_function_call_bad_cvr : Error<"member function %0 not viable: "
"volatile or restrict|const, volatile, or restrict}2">;

def err_reference_bind_to_bitfield : Error<
"%select{non-const|volatile}0 reference cannot bind to bit-field %1">;
"%select{non-const|volatile}0 reference cannot bind to "
"bit-field%select{| %1}2">;
def err_reference_bind_to_vector_element : Error<
"%select{non-const|volatile}0 reference cannot bind to vector element">;
def err_reference_var_requires_init : Error<
Expand Down Expand Up @@ -4629,6 +4630,9 @@ def err_bad_cxx_cast_generic : Error<
def err_bad_cxx_cast_rvalue : Error<
"%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|"
"functional-style cast}0 from rvalue to reference type %2">;
def err_bad_cxx_cast_bitfield : Error<
"%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|"
"functional-style cast}0 from bit-field lvalue to reference type %2">;
def err_bad_cxx_cast_qualifiers_away : Error<
"%select{const_cast|static_cast|reinterpret_cast|dynamic_cast|C-style cast|"
"functional-style cast}0 from %1 to %2 casts away qualifiers">;
Expand Down
2 changes: 1 addition & 1 deletion lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4277,7 +4277,7 @@ QualType ASTContext::isPromotableBitField(Expr *E) const {
if (E->isTypeDependent() || E->isValueDependent())
return QualType();

FieldDecl *Field = E->getBitField();
FieldDecl *Field = E->getSourceBitField(); // FIXME: conditional bit-fields?
if (!Field)
return QualType();

Expand Down
11 changes: 8 additions & 3 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3149,7 +3149,7 @@ bool Expr::isObjCSelfExpr() const {
return M->getSelfDecl() == Param;
}

FieldDecl *Expr::getBitField() {
FieldDecl *Expr::getSourceBitField() {
Expr *E = this->IgnoreParens();

while (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
Expand All @@ -3165,17 +3165,22 @@ FieldDecl *Expr::getBitField() {
if (Field->isBitField())
return Field;

if (ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E))
if (FieldDecl *Ivar = dyn_cast<FieldDecl>(IvarRef->getDecl()))
if (Ivar->isBitField())
return Ivar;

if (DeclRefExpr *DeclRef = dyn_cast<DeclRefExpr>(E))
if (FieldDecl *Field = dyn_cast<FieldDecl>(DeclRef->getDecl()))
if (Field->isBitField())
return Field;

if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E)) {
if (BinOp->isAssignmentOp() && BinOp->getLHS())
return BinOp->getLHS()->getBitField();
return BinOp->getLHS()->getSourceBitField();

if (BinOp->getOpcode() == BO_Comma && BinOp->getRHS())
return BinOp->getRHS()->getBitField();
return BinOp->getRHS()->getSourceBitField();
}

return 0;
Expand Down
12 changes: 11 additions & 1 deletion lib/Sema/SemaCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1452,14 +1452,24 @@ static TryCastResult TryConstCast(Sema &Self, Expr *SrcExpr, QualType DestType,
DestType = Self.Context.getCanonicalType(DestType);
QualType SrcType = SrcExpr->getType();
if (const ReferenceType *DestTypeTmp =DestType->getAs<ReferenceType>()) {
if (DestTypeTmp->isLValueReferenceType() && !SrcExpr->isLValue()) {
if (isa<LValueReferenceType>(DestTypeTmp) && !SrcExpr->isLValue()) {
// Cannot const_cast non-lvalue to lvalue reference type. But if this
// is C-style, static_cast might find a way, so we simply suggest a
// message and tell the parent to keep searching.
msg = diag::err_bad_cxx_cast_rvalue;
return TC_NotApplicable;
}

// It's not completely clear under the standard whether we can
// const_cast bit-field gl-values. Doing so would not be
// intrinsically complicated, but for now, we say no for
// consistency with other compilers and await the word of the
// committee.
if (SrcExpr->refersToBitField()) {
msg = diag::err_bad_cxx_cast_bitfield;
return TC_NotApplicable;
}

// C++ 5.2.11p4: An lvalue of type T1 can be [cast] to an lvalue of type T2
// [...] if a pointer to T1 can be [cast] to the type pointer to T2.
DestType = Self.Context.getPointerType(DestTypeTmp->getPointeeType());
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4311,7 +4311,7 @@ static IntRange GetExprRange(ASTContext &C, Expr *E, unsigned MaxWidth) {
IntRange::forValueOfType(C, E->getType());
}

if (FieldDecl *BitField = E->getBitField())
if (FieldDecl *BitField = E->getSourceBitField())
return IntRange(BitField->getBitWidthValue(C),
BitField->getType()->isUnsignedIntegerOrEnumerationType());

Expand Down Expand Up @@ -4688,7 +4688,7 @@ static void AnalyzeAssignment(Sema &S, BinaryOperator *E) {

// We want to recurse on the RHS as normal unless we're assigning to
// a bitfield.
if (FieldDecl *Bitfield = E->getLHS()->getBitField()) {
if (FieldDecl *Bitfield = E->getLHS()->getSourceBitField()) {
if (AnalyzeBitFieldAssignment(S, Bitfield, E->getRHS(),
E->getOperatorLoc())) {
// Recurse, ignoring any implicit conversions on the RHS.
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3383,7 +3383,7 @@ Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, SourceLocation OpLoc,
isInvalid = CheckAlignOfExpr(*this, E);
} else if (ExprKind == UETT_VecStep) {
isInvalid = CheckVecStepExpr(E);
} else if (E->getBitField()) { // C99 6.5.3.4p1.
} else if (E->refersToBitField()) { // C99 6.5.3.4p1.
Diag(E->getExprLoc(), diag::err_sizeof_alignof_bitfield) << 0;
isInvalid = true;
} else {
Expand Down
15 changes: 10 additions & 5 deletions lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3440,7 +3440,7 @@ convertQualifiersAndValueKindIfNecessary(Sema &S,
Qualifiers T1Quals,
Qualifiers T2Quals,
bool IsLValueRef) {
bool IsNonAddressableType = Initializer->getBitField() ||
bool IsNonAddressableType = Initializer->refersToBitField() ||
Initializer->refersToVectorElement();

if (IsNonAddressableType) {
Expand Down Expand Up @@ -5225,13 +5225,18 @@ InitializationSequence::Perform(Sema &S,
}

case SK_BindReference:
if (FieldDecl *BitField = CurInit.get()->getBitField()) {
// References cannot bind to bit fields (C++ [dcl.init.ref]p5).
// References cannot bind to bit-fields (C++ [dcl.init.ref]p5).
if (CurInit.get()->refersToBitField()) {
// We don't necessarily have an unambiguous source bit-field.
FieldDecl *BitField = CurInit.get()->getSourceBitField();
S.Diag(Kind.getLocation(), diag::err_reference_bind_to_bitfield)
<< Entity.getType().isVolatileQualified()
<< BitField->getDeclName()
<< (BitField ? BitField->getDeclName() : DeclarationName())
<< (BitField != NULL)
<< CurInit.get()->getSourceRange();
S.Diag(BitField->getLocation(), diag::note_bitfield_decl);
if (BitField)
S.Diag(BitField->getLocation(), diag::note_bitfield_decl);

return ExprError();
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1837,7 +1837,7 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType) {
// conversion.
using llvm::APSInt;
if (From)
if (FieldDecl *MemberDecl = From->getBitField()) {
if (FieldDecl *MemberDecl = From->getSourceBitField()) {
APSInt BitWidth;
if (FromType->isIntegralType(Context) &&
MemberDecl->getBitWidth()->isIntegerConstantExpr(BitWidth, Context)) {
Expand Down
23 changes: 23 additions & 0 deletions test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,26 @@ namespace PR6066 {
return 0;
}
}

namespace test3 {
struct A {
unsigned bitX : 4; // expected-note 4 {{bit-field is declared here}}
unsigned bitY : 4; // expected-note {{bit-field is declared here}}
unsigned var;

void foo();
};

void test(A *a) {
unsigned &t0 = a->bitX; // expected-error {{non-const reference cannot bind to bit-field 'bitX'}}
unsigned &t1 = (unsigned&) a->bitX; // expected-error {{non-const reference cannot bind to bit-field 'bitX'}}
unsigned &t2 = const_cast<unsigned&>(a->bitX); // expected-error {{const_cast from bit-field lvalue to reference type 'unsigned int &'}}
unsigned &t3 = (a->foo(), a->bitX); // expected-error {{non-const reference cannot bind to bit-field 'bitX'}}
unsigned &t4 = (a->var ? a->bitX : a->bitY); // expected-error {{non-const reference cannot bind to bit-field}}
unsigned &t5 = (a->var ? a->bitX : a->bitX); // expected-error {{non-const reference cannot bind to bit-field}}
unsigned &t6 = (a->var ? a->bitX : a->var); // expected-error {{non-const reference cannot bind to bit-field}}
unsigned &t7 = (a->var ? a->var : a->bitY); // expected-error {{non-const reference cannot bind to bit-field}}
unsigned &t8 = (a->bitX = 3); // expected-error {{non-const reference cannot bind to bit-field 'bitX'}}
unsigned &t9 = (a->bitY += 3); // expected-error {{non-const reference cannot bind to bit-field 'bitY'}}
}
}
17 changes: 16 additions & 1 deletion test/CXX/expr/expr.post/expr.const.cast/p1-0x.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
// expected-no-diagnostics

// The result of the expression const_cast<T>(v) is of type T. If T is
// an lvalue reference to object type, the result is an lvalue; if T
Expand All @@ -16,3 +15,19 @@ void test_classification(const int *ptr) {
int *ptr1 = const_cast<int *&&>(xvalue<const int*>());
int *ptr2 = const_cast<int *&&>(prvalue<const int*>());
}

struct A {
volatile unsigned ubf : 4;
volatile unsigned uv;
volatile int sv;
void foo();
bool pred();
};

void test(A &a) {
unsigned &t0 = const_cast<unsigned&>(a.ubf); // expected-error {{const_cast from bit-field lvalue to reference type}}
unsigned &t1 = const_cast<unsigned&>(a.foo(), a.ubf); // expected-error {{const_cast from bit-field lvalue to reference type}}
unsigned &t2 = const_cast<unsigned&>(a.pred() ? a.ubf : a.ubf); // expected-error {{const_cast from bit-field lvalue to reference type}}
unsigned &t3 = const_cast<unsigned&>(a.pred() ? a.ubf : a.uv); // expected-error {{const_cast from bit-field lvalue to reference type}}
unsigned &t4 = const_cast<unsigned&>(a.pred() ? a.ubf : a.sv); // expected-error {{const_cast from rvalue to reference type}}
}
20 changes: 20 additions & 0 deletions test/CXX/expr/expr.unary/expr.sizeof/p1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s

struct A {
unsigned bitX : 4;
unsigned bitY : 4;
unsigned var;

void foo();
};

void test(A *a) {
int x;
x = sizeof(a->bitX); // expected-error {{invalid application of 'sizeof' to bit-field}}
x = sizeof((unsigned) a->bitX);
x = sizeof(a->foo(), a->bitX); // expected-error {{invalid application of 'sizeof' to bit-field}}
x = sizeof(a->var ? a->bitX : a->bitY); // expected-error {{invalid application of 'sizeof' to bit-field}}
x = sizeof(a->var ? a->bitX : a->bitX); // expected-error {{invalid application of 'sizeof' to bit-field}}
x = sizeof(a->bitX = 3); // expected-error {{invalid application of 'sizeof' to bit-field}}
x = sizeof(a->bitY += 3); // expected-error {{invalid application of 'sizeof' to bit-field}}
}
15 changes: 15 additions & 0 deletions test/Sema/bitfield.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,18 @@ int y;
struct PR8025 {
double : 2; // expected-error{{anonymous bit-field has non-integral type 'double'}}
};

struct Test4 {
unsigned bitX : 4;
unsigned bitY : 4;
unsigned var;
};
void test4(struct Test4 *t) {
(void) sizeof(t->bitX); // expected-error {{invalid application of 'sizeof' to bit-field}}
(void) sizeof((t->bitY)); // expected-error {{invalid application of 'sizeof' to bit-field}}
(void) sizeof(t->bitX = 4); // not a bitfield designator in C
(void) sizeof(t->bitX += 4); // not a bitfield designator in C
(void) sizeof((void) 0, t->bitX); // not a bitfield designator in C
(void) sizeof(t->var ? t->bitX : t->bitY); // not a bitfield designator in C
(void) sizeof(t->var ? t->bitX : t->bitX); // not a bitfield designator in C
}
19 changes: 16 additions & 3 deletions test/SemaObjCXX/references.mm
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 -verify -emit-llvm -o - %s
// expected-no-diagnostics
// RUN: %clang_cc1 -verify -o - %s

__attribute__((objc_root_class))
@interface Root @end

// Test reference binding.

Expand All @@ -8,7 +10,7 @@
int f1;
} T;

@interface A
@interface A : Root
@property (assign) T p0;
@property (assign) T& p1;
@end
Expand Down Expand Up @@ -61,3 +63,14 @@ void f6(baz* x) {
f5d(ToBar());
(void)((foo&)ToBar());
}

// rdar://13794269
@interface B : Root @end
@implementation B {
unsigned bf : 4; // expected-note {{declared here}}
}

- (void) foo {
unsigned &i = bf; // expected-error {{non-const reference cannot bind to bit-field 'bf'}}
}
@end

0 comments on commit 993f43f

Please sign in to comment.