Properly implement the scope restriction on the NRVO for
throw-expressions, such that we don't consider the NRVO when the
non-volatile automatic object comes from outside the innermost try
scope (C++0x [class.copymove]p13). In C++98/03, our ASTs were
incorrect but it didn't matter because IR generation doesn't actually
apply the NRVO here. In C++0x, however, we were moving from an object
when in fact we should have copied from it. Fixes PR10142 /
<rdar://problem/9714312>.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@134548 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 6822681..b32eeda 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -787,12 +787,12 @@
case tok::r_brace:
case tok::colon:
case tok::comma:
- return Actions.ActOnCXXThrow(ThrowLoc, 0);
+ return Actions.ActOnCXXThrow(getCurScope(), ThrowLoc, 0);
default:
ExprResult Expr(ParseAssignmentExpression());
if (Expr.isInvalid()) return move(Expr);
- return Actions.ActOnCXXThrow(ThrowLoc, Expr.take());
+ return Actions.ActOnCXXThrow(getCurScope(), ThrowLoc, Expr.take());
}
}
diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp
index 28b34fe..47a9ecc 100644
--- a/lib/Parse/ParseStmt.cpp
+++ b/lib/Parse/ParseStmt.cpp
@@ -647,6 +647,10 @@
SubStmt.get(), getCurScope());
}
+StmtResult Parser::ParseCompoundStatement(ParsedAttributes &Attr,
+ bool isStmtExpr) {
+ return ParseCompoundStatement(Attr, isStmtExpr, Scope::DeclScope);
+}
/// ParseCompoundStatement - Parse a "{}" block.
///
@@ -676,14 +680,15 @@
/// [OMP] flush-directive
///
StmtResult Parser::ParseCompoundStatement(ParsedAttributes &attrs,
- bool isStmtExpr) {
+ bool isStmtExpr,
+ unsigned ScopeFlags) {
//FIXME: Use attributes?
assert(Tok.is(tok::l_brace) && "Not a compount stmt!");
// Enter a scope to hold everything within the compound stmt. Compound
// statements can always hold declarations.
- ParseScope CompoundScope(this, Scope::DeclScope);
+ ParseScope CompoundScope(this, ScopeFlags);
// Parse the statements in the body.
return ParseCompoundStatementBody(isStmtExpr);
@@ -1910,7 +1915,8 @@
return StmtError(Diag(Tok, diag::err_expected_lbrace));
// FIXME: Possible draft standard bug: attribute-specifier should be allowed?
ParsedAttributesWithRange attrs(AttrFactory);
- StmtResult TryBlock(ParseCompoundStatement(attrs));
+ StmtResult TryBlock(ParseCompoundStatement(attrs, /*isStmtExpr=*/false,
+ Scope::DeclScope|Scope::TryScope));
if (TryBlock.isInvalid())
return move(TryBlock);
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index 8dc9cee..6ae48dd 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -480,23 +480,63 @@
/// ActOnCXXThrow - Parse throw expressions.
ExprResult
-Sema::ActOnCXXThrow(SourceLocation OpLoc, Expr *Ex) {
+Sema::ActOnCXXThrow(Scope *S, SourceLocation OpLoc, Expr *Ex) {
+ bool IsThrownVarInScope = false;
+ if (Ex) {
+ // C++0x [class.copymove]p31:
+ // When certain criteria are met, an implementation is allowed to omit the
+ // copy/move construction of a class object [...]
+ //
+ // - in a throw-expression, when the operand is the name of a
+ // non-volatile automatic object (other than a function or catch-
+ // clause parameter) whose scope does not extend beyond the end of the
+ // innermost enclosing try-block (if there is one), the copy/move
+ // operation from the operand to the exception object (15.1) can be
+ // omitted by constructing the automatic object directly into the
+ // exception object
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex->IgnoreParens()))
+ if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) {
+ if (Var->hasLocalStorage() && !Var->getType().isVolatileQualified()) {
+ for( ; S; S = S->getParent()) {
+ if (S->isDeclScope(Var)) {
+ IsThrownVarInScope = true;
+ break;
+ }
+
+ if (S->getFlags() &
+ (Scope::FnScope | Scope::ClassScope | Scope::BlockScope |
+ Scope::FunctionPrototypeScope | Scope::ObjCMethodScope |
+ Scope::TryScope))
+ break;
+ }
+ }
+ }
+ }
+
+ return BuildCXXThrow(OpLoc, Ex, IsThrownVarInScope);
+}
+
+ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex,
+ bool IsThrownVarInScope) {
// Don't report an error if 'throw' is used in system headers.
if (!getLangOptions().CXXExceptions &&
!getSourceManager().isInSystemHeader(OpLoc))
Diag(OpLoc, diag::err_exceptions_disabled) << "throw";
-
+
if (Ex && !Ex->isTypeDependent()) {
- ExprResult ExRes = CheckCXXThrowOperand(OpLoc, Ex);
+ ExprResult ExRes = CheckCXXThrowOperand(OpLoc, Ex, IsThrownVarInScope);
if (ExRes.isInvalid())
return ExprError();
Ex = ExRes.take();
}
- return Owned(new (Context) CXXThrowExpr(Ex, Context.VoidTy, OpLoc));
+
+ return Owned(new (Context) CXXThrowExpr(Ex, Context.VoidTy, OpLoc,
+ IsThrownVarInScope));
}
/// CheckCXXThrowOperand - Validate the operand of a throw.
-ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E) {
+ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E,
+ bool IsThrownVarInScope) {
// C++ [except.throw]p3:
// A throw-expression initializes a temporary object, called the exception
// object, the type of which is determined by removing any top-level
@@ -535,14 +575,28 @@
// Initialize the exception result. This implicitly weeds out
// abstract types or types with inaccessible copy constructors.
- const VarDecl *NRVOVariable = getCopyElisionCandidate(QualType(), E, false);
-
- // FIXME: Determine whether we can elide this copy per C++0x [class.copy]p32.
+
+ // C++0x [class.copymove]p31:
+ // When certain criteria are met, an implementation is allowed to omit the
+ // copy/move construction of a class object [...]
+ //
+ // - in a throw-expression, when the operand is the name of a
+ // non-volatile automatic object (other than a function or catch-clause
+ // parameter) whose scope does not extend beyond the end of the
+ // innermost enclosing try-block (if there is one), the copy/move
+ // operation from the operand to the exception object (15.1) can be
+ // omitted by constructing the automatic object directly into the
+ // exception object
+ const VarDecl *NRVOVariable = 0;
+ if (IsThrownVarInScope)
+ NRVOVariable = getCopyElisionCandidate(QualType(), E, false);
+
InitializedEntity Entity =
InitializedEntity::InitializeException(ThrowLoc, E->getType(),
- /*NRVO=*/false);
+ /*NRVO=*/NRVOVariable != 0);
Res = PerformMoveOrCopyInitialization(Entity, NRVOVariable,
- QualType(), E);
+ QualType(), E,
+ IsThrownVarInScope);
if (Res.isInvalid())
return ExprError();
E = Res.take();
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 164c0ce..65f431d 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -1543,7 +1543,8 @@
Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity,
const VarDecl *NRVOCandidate,
QualType ResultType,
- Expr *Value) {
+ Expr *Value,
+ bool AllowNRVO) {
// C++0x [class.copy]p33:
// When the criteria for elision of a copy operation are met or would
// be met save for the fact that the source object is a function
@@ -1551,7 +1552,8 @@
// overload resolution to select the constructor for the copy is first
// performed as if the object were designated by an rvalue.
ExprResult Res = ExprError();
- if (NRVOCandidate || getCopyElisionCandidate(ResultType, Value, true)) {
+ if (AllowNRVO &&
+ (NRVOCandidate || getCopyElisionCandidate(ResultType, Value, true))) {
ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack,
Value->getType(), CK_LValueToRValue,
Value, VK_XValue);
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 66ea7df..040ba9e 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -1866,8 +1866,9 @@
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
- ExprResult RebuildCXXThrowExpr(SourceLocation ThrowLoc, Expr *Sub) {
- return getSema().ActOnCXXThrow(ThrowLoc, Sub);
+ ExprResult RebuildCXXThrowExpr(SourceLocation ThrowLoc, Expr *Sub,
+ bool IsThrownVariableInScope) {
+ return getSema().BuildCXXThrow(ThrowLoc, Sub, IsThrownVariableInScope);
}
/// \brief Build a new C++ default-argument expression.
@@ -6793,7 +6794,8 @@
SubExpr.get() == E->getSubExpr())
return SemaRef.Owned(E);
- return getDerived().RebuildCXXThrowExpr(E->getThrowLoc(), SubExpr.get());
+ return getDerived().RebuildCXXThrowExpr(E->getThrowLoc(), SubExpr.get(),
+ E->isThrownVariableInScope());
}
template<typename Derived>
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 1697b3b..a14038d 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -1172,8 +1172,9 @@
void ASTStmtReader::VisitCXXThrowExpr(CXXThrowExpr *E) {
VisitExpr(E);
- E->setThrowLoc(ReadSourceLocation(Record, Idx));
- E->setSubExpr(Reader.ReadSubExpr());
+ E->ThrowLoc = ReadSourceLocation(Record, Idx);
+ E->Op = Reader.ReadSubExpr();
+ E->IsThrownVariableInScope = Record[Idx++];
}
void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index 91c0b3d..d0e09a5 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -1174,6 +1174,7 @@
VisitExpr(E);
Writer.AddSourceLocation(E->getThrowLoc(), Record);
Writer.AddStmt(E->getSubExpr());
+ Record.push_back(E->isThrownVariableInScope());
Code = serialization::EXPR_CXX_THROW;
}