From: Richard Smith Date: Mon, 3 Jun 2013 00:17:11 +0000 (+0000) Subject: Fix handling of pointers-to-members and comma expressions when X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4e43dec4df9d7dd8e07b47bb15967f1b733a9bc6;p=clang Fix handling of pointers-to-members and comma expressions when lifetime-extending temporaries in reference bindings. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@183089 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 9509456839..ee0a3fd145 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -760,10 +760,10 @@ public: /// Walk outwards from an expression we want to bind a reference to and /// find the expression whose lifetime needs to be extended. Record - /// the adjustments needed along the path. - const Expr * - skipRValueSubobjectAdjustments( - SmallVectorImpl &Adjustments) const; + /// the LHSs of comma expressions and adjustments needed along the path. + const Expr *skipRValueSubobjectAdjustments( + SmallVectorImpl &CommaLHS, + SmallVectorImpl &Adjustments) const; /// Skip irrelevant expressions to find what should be materialize for /// binding with a reference. diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index f587dfa93e..2a9b9eb453 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -50,9 +50,9 @@ const CXXRecordDecl *Expr::getBestDynamicClassType() const { return cast(D); } -const Expr * -Expr::skipRValueSubobjectAdjustments( - SmallVectorImpl &Adjustments) const { +const Expr *Expr::skipRValueSubobjectAdjustments( + SmallVectorImpl &CommaLHSs, + SmallVectorImpl &Adjustments) const { const Expr *E = this; while (true) { E = E->IgnoreParens(); @@ -88,6 +88,11 @@ Expr::skipRValueSubobjectAdjustments( const MemberPointerType *MPT = BO->getRHS()->getType()->getAs(); Adjustments.push_back(SubobjectAdjustment(MPT, BO->getRHS())); + continue; + } else if (BO->getOpcode() == BO_Comma) { + CommaLHSs.push_back(BO->getLHS()); + E = BO->getRHS(); + continue; } } diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 8ba5a1ce52..28eae50832 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -295,8 +295,13 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, return ReferenceTemporary; } + SmallVector CommaLHSs; SmallVector Adjustments; - E = E->skipRValueSubobjectAdjustments(Adjustments); + E = E->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); + + for (unsigned I = 0, N = CommaLHSs.size(); I != N; ++I) + CGF.EmitIgnoredExpr(CommaLHSs[I]); + if (const OpaqueValueExpr *opaque = dyn_cast(E)) if (opaque->getType()->isRecordType()) return CGF.EmitOpaqueValueLValue(opaque).getAddress(); @@ -332,6 +337,10 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, RValue RV = CGF.EmitAnyExpr(E, AggSlot); + // FIXME: This is wrong. We need to register the destructor for the temporary + // now, *before* we perform the adjustments, because in the case of a + // pointer-to-member adjustment, the adjustment might throw. + // 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. diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp index 5f92cfffc6..8e446f8b9b 100644 --- a/lib/Sema/JumpDiagnostics.cpp +++ b/lib/Sema/JumpDiagnostics.cpp @@ -172,14 +172,19 @@ static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) { if (EWC) Init = EWC->getSubExpr(); + // FIXME: Why are we looking through reference initialization? + // This causes us to incorrectly accept invalid code such as: + // struct S { int n; }; + // int f() { goto x; S &&s = S(); x: return x.n; } const MaterializeTemporaryExpr *M = NULL; Init = Init->findMaterializedTemporary(M); + SmallVector CommaLHSs; SmallVector Adjustments; - Init = Init->skipRValueSubobjectAdjustments(Adjustments); + Init = Init->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments); QualType QT = Init->getType(); - if (QT.isNull()) + if (QT.isNull() || !CommaLHSs.empty()) return ScopePair(diag::note_protected_by_variable_init, 0); const Type *T = QT.getTypePtr(); diff --git a/test/CodeGenCXX/temporaries.cpp b/test/CodeGenCXX/temporaries.cpp index a369c2e369..ea87e7f8e3 100644 --- a/test/CodeGenCXX/temporaries.cpp +++ b/test/CodeGenCXX/temporaries.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 | FileCheck %s struct A { A(); ~A(); @@ -558,3 +558,49 @@ namespace AssignmentOp { // CHECK: call {{.*}} @_ZN12AssignmentOp1BaSERKS // CHECK: call {{.*}} @_ZN12AssignmentOp1AD1Ev( } + +namespace BindToSubobject { + struct A { + A(); + ~A(); + int a; + }; + + void f(), g(); + + // CHECK: call void @_ZN15BindToSubobject1AC1Ev({{.*}} @_ZGRN15BindToSubobject1aE) + // CHECK: call i32 @__cxa_atexit({{.*}} bitcast ({{.*}} @_ZN15BindToSubobject1AD1Ev to void (i8*)*), i8* bitcast ({{.*}} @_ZGRN15BindToSubobject1aE to i8*), i8* @__dso_handle) + // CHECK: store i32* getelementptr inbounds ({{.*}} @_ZGRN15BindToSubobject1aE, i32 0, i32 0), i32** @_ZN15BindToSubobject1aE, align 8 + int &&a = A().a; + + // CHECK: call void @_ZN15BindToSubobject1fEv() + // CHECK: call void @_ZN15BindToSubobject1AC1Ev({{.*}} @_ZGRN15BindToSubobject1bE) + // CHECK: call i32 @__cxa_atexit({{.*}} bitcast ({{.*}} @_ZN15BindToSubobject1AD1Ev to void (i8*)*), i8* bitcast ({{.*}} @_ZGRN15BindToSubobject1bE to i8*), i8* @__dso_handle) + // CHECK: store i32* getelementptr inbounds ({{.*}} @_ZGRN15BindToSubobject1bE, i32 0, i32 0), i32** @_ZN15BindToSubobject1bE, align 8 + int &&b = (f(), A().a); + + int A::*h(); + + // CHECK: call void @_ZN15BindToSubobject1fEv() + // CHECK: call void @_ZN15BindToSubobject1gEv() + // CHECK: call void @_ZN15BindToSubobject1AC1Ev({{.*}} @_ZGRN15BindToSubobject1cE) + // FIXME: This is wrong. We should emit the call to __cxa_atexit prior to + // calling h(), in case h() throws. + // CHECK: call {{.*}} @_ZN15BindToSubobject1hE + // CHECK: getelementptr + // CHECK: call i32 @__cxa_atexit({{.*}} bitcast ({{.*}} @_ZN15BindToSubobject1AD1Ev to void (i8*)*), i8* bitcast ({{.*}} @_ZGRN15BindToSubobject1cE to i8*), i8* @__dso_handle) + // CHECK: store i32* {{.*}}, i32** @_ZN15BindToSubobject1cE, align 8 + int &&c = (f(), (g(), A().*h())); + + struct B { + int padding; + A a; + }; + + // CHECK: call void @_ZN15BindToSubobject1BC1Ev({{.*}} @_ZGRN15BindToSubobject1dE) + // CHECK: call {{.*}} @_ZN15BindToSubobject1hE + // CHECK: getelementptr {{.*}} getelementptr + // CHECK: call i32 @__cxa_atexit({{.*}} bitcast ({{.*}} @_ZN15BindToSubobject1BD1Ev to void (i8*)*), i8* bitcast ({{.*}} @_ZGRN15BindToSubobject1dE to i8*), i8* @__dso_handle) + // CHECK: store i32* {{.*}}, i32** @_ZN15BindToSubobject1dE, align 8 + int &&d = (B().a).*h(); +}