From: Douglas Gregor Date: Fri, 29 Jan 2010 19:14:02 +0000 (+0000) Subject: Fix reference binding of const lvalue references to bit-fields, which X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=de4b1d86bf48bc2a84bddf6b188f6da53eaea845;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94826 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 4e05a85027..66f06e090a 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1958,6 +1958,13 @@ bool Expr::isNullPointerConstant(ASTContext &Ctx, FieldDecl *Expr::getBitField() { Expr *E = this->IgnoreParens(); + while (ImplicitCastExpr *ICE = dyn_cast(E)) { + if (ICE->isLvalueCast() && ICE->getCastKind() == CastExpr::CK_NoOp) + E = ICE->getSubExpr()->IgnoreParens(); + else + break; + } + if (MemberExpr *MemRef = dyn_cast(E)) if (FieldDecl *Field = dyn_cast(MemRef->getMemberDecl())) if (Field->isBitField()) diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index a70582dd53..1a588094cd 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -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 //===----------------------------------------------------------------------===// diff --git a/lib/Sema/SemaInit.h b/lib/Sema/SemaInit.h index d7d3756020..0b72fd4532 100644 --- a/lib/Sema/SemaInit.h +++ b/lib/Sema/SemaInit.h @@ -21,6 +21,10 @@ #include "llvm/ADT/SmallVector.h" #include +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 diff --git a/test/CodeGenCXX/reference-init.cpp b/test/CodeGenCXX/reference-init.cpp index 61ae2daffd..9469c84eb5 100644 --- a/test/CodeGenCXX/reference-init.cpp +++ b/test/CodeGenCXX/reference-init.cpp @@ -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;