Promote the static getNRVOCandidate() function, which computed the

NRVO candidate for a return statement, to
Sema::getCopyElisionCandidate(), and teach it enough to also determine
the NRVO candidate for a throw expression. We still don't use the
latter information, however.

Along the way, implement core issue 1148, which eliminates copy
elision from catch parameters and clarifies that copy elision cannot
occur from function parameters (which we already implemented).

llvm-svn: 123982
This commit is contained in:
Douglas Gregor 2011-01-21 18:05:27 +00:00
parent 6381402fe1
commit 5d36900d7a
5 changed files with 48 additions and 23 deletions

View File

@ -1648,6 +1648,9 @@ public:
StmtResult ActOnContinueStmt(SourceLocation ContinueLoc, Scope *CurScope);
StmtResult ActOnBreakStmt(SourceLocation GotoLoc, Scope *CurScope);
const VarDecl *getCopyElisionCandidate(QualType ReturnType, Expr *E,
bool AllowFunctionParameters);
StmtResult ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);
StmtResult ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp);

View File

@ -517,7 +517,7 @@ bool Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *&E) {
// Initialize the exception result. This implicitly weeds out
// abstract types or types with inaccessible copy constructors.
// FIXME: Determine whether we can elide this copy per C++0x [class.copy]p34.
// FIXME: Determine whether we can elide this copy per C++0x [class.copy]p32.
InitializedEntity Entity =
InitializedEntity::InitializeException(ThrowLoc, E->getType(),
/*NRVO=*/false);

View File

@ -3317,7 +3317,7 @@ static ExprResult CopyObject(Sema &S,
if (!Class)
return move(CurInit);
// C++0x [class.copy]p34:
// C++0x [class.copy]p32:
// When certain criteria are met, an implementation is allowed to
// omit the copy/move construction of a class object, even if the
// copy/move constructor and/or destructor for the object have

View File

@ -1089,40 +1089,49 @@ Sema::ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope) {
return Owned(new (Context) BreakStmt(BreakLoc));
}
/// \brief Determine whether a return statement is a candidate for the named
/// return value optimization (C++0x 12.8p34, bullet 1).
/// \brief Determine whether the given expression is a candidate for
/// copy elision in either a return statement or a throw expression.
///
/// \param Ctx The context in which the return expression and type occur.
/// \param ReturnType If we're determining the copy elision candidate for
/// a return statement, this is the return type of the function. If we're
/// determining the copy elision candidate for a throw expression, this will
/// be a NULL type.
///
/// \param RetType The return type of the function or block.
/// \param E The expression being returned from the function or block, or
/// being thrown.
///
/// \param RetExpr The expression being returned from the function or block.
/// \param AllowFunctionParameter
///
/// \returns The NRVO candidate variable, if the return statement may use the
/// NRVO, or NULL if there is no such candidate.
static const VarDecl *getNRVOCandidate(ASTContext &Ctx, QualType RetType,
Expr *RetExpr) {
QualType ExprType = RetExpr->getType();
const VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType,
Expr *E,
bool AllowFunctionParameter) {
QualType ExprType = E->getType();
// - in a return statement in a function with ...
// ... a class return type ...
if (!RetType->isRecordType())
return 0;
// ... the same cv-unqualified type as the function return type ...
if (!Ctx.hasSameUnqualifiedType(RetType, ExprType))
return 0;
// ... the expression is the name of a non-volatile automatic object ...
// We ignore parentheses here.
// FIXME: Is this compliant? (Everyone else does it)
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(RetExpr->IgnoreParens());
if (!ReturnType.isNull()) {
if (!ReturnType->isRecordType())
return 0;
// ... the same cv-unqualified type as the function return type ...
if (!Context.hasSameUnqualifiedType(ReturnType, ExprType))
return 0;
}
// ... the expression is the name of a non-volatile automatic object
// (other than a function or catch-clause parameter)) ...
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E->IgnoreParens());
if (!DR)
return 0;
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
if (!VD)
return 0;
if (VD->getKind() == Decl::Var && VD->hasLocalStorage() &&
if (VD->hasLocalStorage() && !VD->isExceptionVariable() &&
!VD->getType()->isReferenceType() && !VD->hasAttr<BlocksAttr>() &&
!VD->getType().isVolatileQualified())
!VD->getType().isVolatileQualified() &&
(VD->getKind() == Decl::Var ||
AllowFunctionParameter && VD->getKind() == Decl::ParmVar))
return VD;
return 0;
@ -1183,7 +1192,7 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
// In C++ the return statement is handled via a copy initialization.
// the C version of which boils down to CheckSingleAssignmentConstraints.
NRVOCandidate = getNRVOCandidate(Context, FnRetType, RetValExp);
NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, false);
ExprResult Res = PerformCopyInitialization(
InitializedEntity::InitializeResult(ReturnLoc,
FnRetType,
@ -1281,7 +1290,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
// In C++ the return statement is handled via a copy initialization.
// the C version of which boils down to CheckSingleAssignmentConstraints.
NRVOCandidate = getNRVOCandidate(Context, FnRetType, RetValExp);
NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, false);
ExprResult Res = PerformCopyInitialization(
InitializedEntity::InitializeResult(ReturnLoc,
FnRetType,

View File

@ -131,3 +131,16 @@ X test4(bool B) {
// CHECK: tail call void @exit(i32 1)
exit(1);
}
// CHECK-EH: define void @_Z5test5
void may_throw();
X test5() {
try {
may_throw();
} catch (X x) {
// CHECK-EH: invoke void @_ZN1XC1ERKS_
// CHECK-EH: call void @__cxa_end_catch()
// CHECK-EH: ret void
return x;
}
}