Fix reference binding of const lvalue references to bit-fields, which

requires a temporary. Previously, we were building an initialization
sequence that bound to the bit-field as if it were a real lvalue. Note
that we previously (and still) diagnose binding of non-const
references to bit-fields, as we should.

There's no real way to test that this code is correct, since reference
binding does not *currently* have any representation in the AST. This
fix should make it easier for that to happen, so I've verified this
fix with...

Added InitializationSequence::dump(), to print an initialization
sequence for debugging purposes.

llvm-svn: 94826
This commit is contained in:
Douglas Gregor
2010-01-29 19:14:02 +00:00
parent 9fb8ce835d
commit 65eb86e912
4 changed files with 217 additions and 3 deletions

View File

@@ -1958,6 +1958,13 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx,
FieldDecl *Expr::getBitField() {
Expr *E = this->IgnoreParens();
while (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) {
if (ICE->isLvalueCast() && ICE->getCastKind() == CastExpr::CK_NoOp)
E = ICE->getSubExpr()->IgnoreParens();
else
break;
}
if (MemberExpr *MemRef = dyn_cast<MemberExpr>(E))
if (FieldDecl *Field = dyn_cast<FieldDecl>(MemRef->getMemberDecl()))
if (Field->isBitField())

View File

@@ -2327,16 +2327,20 @@ static void TryReferenceInitialization(Sema &S,
// - is an lvalue (but is not a bit-field), and "cv1 T1" is
// reference-compatible with "cv2 T2," or
//
// Per C++ [over.best.ics]p2, we ignore whether the lvalue is a
// Per C++ [over.best.ics]p2, we don't diagnose whether the lvalue is a
// bit-field when we're determining whether the reference initialization
// can occur. This property will be checked by PerformInitialization.
// can occur. However, we do pay attention to whether it is a bit-field
// to decide whether we're actually binding to a temporary created from
// the bit-field.
if (DerivedToBase)
Sequence.AddDerivedToBaseCastStep(
S.Context.getQualifiedType(T1, T2Quals),
/*isLValue=*/true);
if (T1Quals != T2Quals)
Sequence.AddQualificationConversionStep(cv1T1, /*IsLValue=*/true);
Sequence.AddReferenceBindingStep(cv1T1, /*bindingTemporary=*/false);
bool BindingTemporary = T1Quals.hasConst() && !T1Quals.hasVolatile() &&
Initializer->getBitField();
Sequence.AddReferenceBindingStep(cv1T1, BindingTemporary);
return;
}
@@ -3665,6 +3669,194 @@ bool InitializationSequence::Diagnose(Sema &S,
return true;
}
void InitializationSequence::dump(llvm::raw_ostream &OS) const {
switch (SequenceKind) {
case FailedSequence: {
OS << "Failed sequence: ";
switch (Failure) {
case FK_TooManyInitsForReference:
OS << "too many initializers for reference";
break;
case FK_ArrayNeedsInitList:
OS << "array requires initializer list";
break;
case FK_ArrayNeedsInitListOrStringLiteral:
OS << "array requires initializer list or string literal";
break;
case FK_AddressOfOverloadFailed:
OS << "address of overloaded function failed";
break;
case FK_ReferenceInitOverloadFailed:
OS << "overload resolution for reference initialization failed";
break;
case FK_NonConstLValueReferenceBindingToTemporary:
OS << "non-const lvalue reference bound to temporary";
break;
case FK_NonConstLValueReferenceBindingToUnrelated:
OS << "non-const lvalue reference bound to unrelated type";
break;
case FK_RValueReferenceBindingToLValue:
OS << "rvalue reference bound to an lvalue";
break;
case FK_ReferenceInitDropsQualifiers:
OS << "reference initialization drops qualifiers";
break;
case FK_ReferenceInitFailed:
OS << "reference initialization failed";
break;
case FK_ConversionFailed:
OS << "conversion failed";
break;
case FK_TooManyInitsForScalar:
OS << "too many initializers for scalar";
break;
case FK_ReferenceBindingToInitList:
OS << "referencing binding to initializer list";
break;
case FK_InitListBadDestinationType:
OS << "initializer list for non-aggregate, non-scalar type";
break;
case FK_UserConversionOverloadFailed:
OS << "overloading failed for user-defined conversion";
break;
case FK_ConstructorOverloadFailed:
OS << "constructor overloading failed";
break;
case FK_DefaultInitOfConst:
OS << "default initialization of a const variable";
break;
}
OS << '\n';
return;
}
case DependentSequence:
OS << "Dependent sequence: ";
return;
case UserDefinedConversion:
OS << "User-defined conversion sequence: ";
break;
case ConstructorInitialization:
OS << "Constructor initialization sequence: ";
break;
case ReferenceBinding:
OS << "Reference binding: ";
break;
case ListInitialization:
OS << "List initialization: ";
break;
case ZeroInitialization:
OS << "Zero initialization\n";
return;
case NoInitialization:
OS << "No initialization\n";
return;
case StandardConversion:
OS << "Standard conversion: ";
break;
case CAssignment:
OS << "C assignment: ";
break;
case StringInit:
OS << "String initialization: ";
break;
}
for (step_iterator S = step_begin(), SEnd = step_end(); S != SEnd; ++S) {
if (S != step_begin()) {
OS << " -> ";
}
switch (S->Kind) {
case SK_ResolveAddressOfOverloadedFunction:
OS << "resolve address of overloaded function";
break;
case SK_CastDerivedToBaseRValue:
OS << "derived-to-base case (rvalue" << S->Type.getAsString() << ")";
break;
case SK_CastDerivedToBaseLValue:
OS << "derived-to-base case (lvalue" << S->Type.getAsString() << ")";
break;
case SK_BindReference:
OS << "bind reference to lvalue";
break;
case SK_BindReferenceToTemporary:
OS << "bind reference to a temporary";
break;
case SK_UserConversion:
OS << "user-defined conversion via " << S->Function->getNameAsString();
break;
case SK_QualificationConversionRValue:
OS << "qualification conversion (rvalue)";
case SK_QualificationConversionLValue:
OS << "qualification conversion (lvalue)";
break;
case SK_ConversionSequence:
OS << "implicit conversion sequence (";
S->ICS->DebugPrint(); // FIXME: use OS
OS << ")";
break;
case SK_ListInitialization:
OS << "list initialization";
break;
case SK_ConstructorInitialization:
OS << "constructor initialization";
break;
case SK_ZeroInitialization:
OS << "zero initialization";
break;
case SK_CAssignment:
OS << "C assignment";
break;
case SK_StringInit:
OS << "string initialization";
break;
}
}
}
void InitializationSequence::dump() const {
dump(llvm::errs());
}
//===----------------------------------------------------------------------===//
// Initialization helper functions
//===----------------------------------------------------------------------===//

View File

@@ -21,6 +21,10 @@
#include "llvm/ADT/SmallVector.h"
#include <cassert>
namespace llvm {
class raw_ostream;
}
namespace clang {
class CXXBaseSpecifier;
@@ -658,6 +662,14 @@ public:
assert(getKind() == FailedSequence && "Not an initialization failure!");
return Failure;
}
/// \brief Dump a representation of this initialization sequence to
/// the given stream, for debugging purposes.
void dump(llvm::raw_ostream &OS) const;
/// \brief Dump a representation of this initialization sequence to
/// standard error, for debugging purposes.
void dump() const;
};
} // end namespace clang

View File

@@ -19,3 +19,6 @@ namespace PR5911 {
struct Foo { int foo; };
Foo& ignoreSetMutex = *(new Foo);
// Binding to a bit-field that requires a temporary.
struct { int bitfield : 3; } s = { 3 };
const int &s2 = s.bitfield;