diff options
-rw-r--r-- | lib/CodeGen/CGExpr.cpp | 118 | ||||
-rw-r--r-- | test/CodeGenCXX/references.cpp | 55 |
2 files changed, 154 insertions, 19 deletions
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 489a9a2044..80d38f250d 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -135,6 +135,39 @@ void CodeGenFunction::EmitAnyExprToMem(const Expr *E, } } +/// \brief An adjustment to be made to the temporary created when emitting a +/// reference binding, which accesses a particular subobject of that temporary. +struct SubobjectAdjustment { + enum { DerivedToBaseAdjustment, FieldAdjustment } Kind; + + union { + struct { + const CXXBaseSpecifierArray *BasePath; + const CXXRecordDecl *DerivedClass; + } DerivedToBase; + + struct { + FieldDecl *Field; + unsigned CVRQualifiers; + } Field; + }; + + SubobjectAdjustment(const CXXBaseSpecifierArray *BasePath, + const CXXRecordDecl *DerivedClass) + : Kind(DerivedToBaseAdjustment) + { + DerivedToBase.BasePath = BasePath; + DerivedToBase.DerivedClass = DerivedClass; + } + + SubobjectAdjustment(FieldDecl *Field, unsigned CVRQualifiers) + : Kind(FieldAdjustment) + { + this->Field.Field = Field; + this->Field.CVRQualifiers = CVRQualifiers; + } +}; + RValue CodeGenFunction::EmitReferenceBindingToExpr(const Expr* E, bool IsInitializer) { bool ShouldDestroyTemporaries = false; @@ -174,19 +207,34 @@ RValue CodeGenFunction::EmitReferenceBindingToExpr(const Expr* E, PopCXXTemporary(); } } else { - const CXXBaseSpecifierArray *BasePath = 0; - const CXXRecordDecl *DerivedClassDecl = 0; + QualType ResultTy = E->getType(); - if (const CastExpr *CE = - dyn_cast<CastExpr>(E->IgnoreParenNoopCasts(getContext()))) { - if (CE->getCastKind() == CastExpr::CK_DerivedToBase) { - E = CE->getSubExpr(); - - BasePath = &CE->getBasePath(); - DerivedClassDecl = - cast<CXXRecordDecl>(E->getType()->getAs<RecordType>()->getDecl()); + llvm::SmallVector<SubobjectAdjustment, 2> Adjustments; + do { + if (const CastExpr *CE + = dyn_cast<CastExpr>(E->IgnoreParenNoopCasts(getContext()))) { + if (CE->getCastKind() == CastExpr::CK_DerivedToBase) { + E = CE->getSubExpr(); + CXXRecordDecl *Derived + = cast<CXXRecordDecl>(E->getType()->getAs<RecordType>()->getDecl()); + Adjustments.push_back(SubobjectAdjustment(&CE->getBasePath(), + Derived)); + continue; + } + } else if (const MemberExpr *ME + = dyn_cast<MemberExpr>( + E->IgnoreParenNoopCasts(getContext()))) { + if (ME->getBase()->isLvalue(getContext()) != Expr::LV_Valid && + ME->getBase()->getType()->isRecordType()) { + if (FieldDecl *Field = dyn_cast<FieldDecl>(ME->getMemberDecl())) { + E = ME->getBase(); + Adjustments.push_back(SubobjectAdjustment(Field, + E->getType().getCVRQualifiers())); + continue; + } + } } - } + } while (false); Val = EmitAnyExprToTemp(E, /*IsAggLocVolatile=*/false, IsInitializer); @@ -225,13 +273,47 @@ RValue CodeGenFunction::EmitReferenceBindingToExpr(const Expr* E, } } - // Check if need to perform the derived-to-base cast. - if (BasePath) { - llvm::Value *Derived = Val.getAggregateAddr(); - llvm::Value *Base = - GetAddressOfBaseClass(Derived, DerivedClassDecl, *BasePath, - /*NullCheckValue=*/false); - return RValue::get(Base); + // Check if need to perform derived-to-base casts and/or field accesses, to + // get from the temporary object we created (and, potentially, for which we + // extended the lifetime) to the subobject we're binding the reference to. + if (!Adjustments.empty()) { + llvm::Value *Object = Val.getAggregateAddr(); + for (unsigned I = Adjustments.size(); I != 0; --I) { + SubobjectAdjustment &Adjustment = Adjustments[I-1]; + switch (Adjustment.Kind) { + case SubobjectAdjustment::DerivedToBaseAdjustment: + Object = GetAddressOfBaseClass(Object, + Adjustment.DerivedToBase.DerivedClass, + *Adjustment.DerivedToBase.BasePath, + /*NullCheckValue=*/false); + break; + + case SubobjectAdjustment::FieldAdjustment: { + unsigned CVR = Adjustment.Field.CVRQualifiers; + LValue LV = EmitLValueForField(Object, Adjustment.Field.Field, CVR); + if (LV.isSimple()) { + Object = LV.getAddress(); + break; + } + + // For non-simple lvalues, we actually have to create a copy of + // the object we're binding to. + QualType T = Adjustment.Field.Field->getType().getNonReferenceType() + .getUnqualifiedType(); + Object = CreateTempAlloca(ConvertType(T), "lv"); + EmitStoreThroughLValue(EmitLoadOfLValue(LV, T), + LValue::MakeAddr(Object, + Qualifiers::fromCVRMask(CVR)), + T); + break; + } + } + } + + const llvm::Type *ResultPtrTy + = llvm::PointerType::get(ConvertType(ResultTy), 0); + Object = Builder.CreateBitCast(Object, ResultPtrTy, "temp"); + return RValue::get(Object); } } diff --git a/test/CodeGenCXX/references.cpp b/test/CodeGenCXX/references.cpp index 3ae1e474f8..ed162492b3 100644 --- a/test/CodeGenCXX/references.cpp +++ b/test/CodeGenCXX/references.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin -verify -emit-llvm -o - %s | FileCheck %s void t1() { extern int& a; int b = a; @@ -168,3 +168,56 @@ namespace N1 { const int* const args_array[] = { &arg }; } } + +// Bind to subobjects while extending the life of the complete object. +namespace N2 { + class X { + public: + X(const X&); + X &operator=(const X&); + ~X(); + }; + + struct P { + X first; + }; + + P getP(); + + // CHECK: define void @_ZN2N21fEi + // CHECK: call void @_ZN2N24getPEv + // CHECK: getelementptr inbounds + // CHECK: store i32 17 + // CHECK: call void @_ZN2N21PD1Ev + void f(int i) { + const X& xr = getP().first; + i = 17; + } + + struct SpaceWaster { + int i, j; + }; + + struct ReallyHasX { + X x; + }; + + struct HasX : ReallyHasX { }; + + struct HasXContainer { + HasX has; + }; + + struct Y : SpaceWaster, HasXContainer { }; + struct Z : SpaceWaster, Y { }; + + Z getZ(); + + // CHECK: define void @_ZN2N21gEi + // CHECK: call void @_ZN2N24getZEv + // FIXME: Not treated as an lvalue! + void g(int i) { + const X &xr = getZ().has.x; + i = 19; + } +} |