From e2b6833d445c7a4ce64f1816c05f176ba1740aca Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Sun, 12 Apr 2009 17:16:29 +0000 Subject: [PATCH] Parse deleted member functions. Parsing member declarations goes through a different code path that I forgot previously. Implement the rvalue reference overload dance for returning local objects. Returning a local object first tries to find a move constructor now. The error message when no move constructor is defined (or is not applicable) and the copy constructor is deleted is quite ugly, though. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68902 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Action.h | 6 ++-- lib/Parse/ParseDeclCXX.cpp | 22 +++++++++--- lib/Sema/Sema.h | 28 +++++++++------ lib/Sema/SemaDeclCXX.cpp | 10 ++++-- lib/Sema/SemaExprCXX.cpp | 20 ++++++++--- lib/Sema/SemaOverload.cpp | 60 ++++++++++++++++++++----------- lib/Sema/SemaStmt.cpp | 47 ++++++++++++++++++++++-- test/SemaCXX/deleted-function.cpp | 12 +++++++ test/SemaCXX/rval-references.cpp | 37 +++++++++++++++++++ 9 files changed, 193 insertions(+), 49 deletions(-) diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 70c21f0a34..699d571146 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -1038,11 +1038,13 @@ public: /// declarator is parsed. 'AS' is the access specifier, 'BitfieldWidth' /// specifies the bitfield width if there is one and 'Init' specifies the /// initializer if any. 'LastInGroup' is non-null for cases where one declspec - /// has multiple declarators on it. + /// has multiple declarators on it. 'Deleted' is true if there's a =delete + /// specifier on the function. virtual DeclPtrTy ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, ExprTy *BitfieldWidth, - ExprTy *Init) { + ExprTy *Init, + bool Deleted = false) { return DeclPtrTy(); } diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index bef1fb71d4..91c05669b2 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -668,7 +668,7 @@ AccessSpecifier Parser::getAccessSpecifierIfPresent() const /// declarator constant-initializer[opt] /// identifier[opt] ':' constant-expression /// -/// pure-specifier: [TODO] +/// pure-specifier: /// '= 0' /// /// constant-initializer: @@ -767,6 +767,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { llvm::SmallVector DeclsInGroup; OwningExprResult BitfieldSize(Actions); OwningExprResult Init(Actions); + bool Deleted = false; while (1) { @@ -787,12 +788,21 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { // // constant-initializer: // '=' constant-expression + // + // defaulted/deleted function-definition: + // '=' 'default' [TODO] + // '=' 'delete' if (Tok.is(tok::equal)) { ConsumeToken(); - Init = ParseInitializer(); - if (Init.isInvalid()) - SkipUntil(tok::comma, true, true); + if (getLang().CPlusPlus0x && Tok.is(tok::kw_delete)) { + ConsumeToken(); + Deleted = true; + } else { + Init = ParseInitializer(); + if (Init.isInvalid()) + SkipUntil(tok::comma, true, true); + } } // If attributes exist after the declarator, parse them. @@ -808,7 +818,8 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { DeclPtrTy ThisDecl = Actions.ActOnCXXMemberDeclarator(CurScope, AS, DeclaratorInfo, BitfieldSize.release(), - Init.release()); + Init.release(), + Deleted); if (ThisDecl) DeclsInGroup.push_back(ThisDecl); @@ -858,6 +869,7 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS) { DeclaratorInfo.clear(); BitfieldSize = 0; Init = 0; + Deleted = false; // Attributes are only allowed on the second declarator. if (Tok.is(tok::kw___attribute)) { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 5018833267..e8eb960a3f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -504,7 +504,8 @@ public: ImplicitConversionSequence TryImplicitConversion(Expr* From, QualType ToType, bool SuppressUserConversions = false, - bool AllowExplicit = false); + bool AllowExplicit = false, + bool ForceRValue = false); bool IsStandardConversion(Expr *From, QualType ToType, StandardConversionSequence& SCS); bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType); @@ -522,7 +523,7 @@ public: bool IsUserDefinedConversion(Expr *From, QualType ToType, UserDefinedConversionSequence& User, bool AllowConversionFunctions, - bool AllowExplicit); + bool AllowExplicit, bool ForceRValue); ImplicitConversionSequence::CompareKind CompareImplicitConversionSequences(const ImplicitConversionSequence& ICS1, @@ -542,9 +543,10 @@ public: ImplicitConversionSequence TryCopyInitialization(Expr* From, QualType ToType, - bool SuppressUserConversions = false); + bool SuppressUserConversions = false, + bool ForceRValue = false); bool PerformCopyInitialization(Expr *&From, QualType ToType, - const char *Flavor); + const char *Flavor, bool Elidable = false); ImplicitConversionSequence TryObjectArgumentInitialization(Expr *From, CXXMethodDecl *Method); @@ -569,7 +571,8 @@ public: void AddOverloadCandidate(FunctionDecl *Function, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, - bool SuppressUserConversions = false); + bool SuppressUserConversions = false, + bool ForceRValue = false); void AddFunctionCandidates(const FunctionSet &Functions, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, @@ -577,7 +580,8 @@ public: void AddMethodCandidate(CXXMethodDecl *Method, Expr *Object, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, - bool SuppressUserConversions = false); + bool SuppressUserConversions = false, + bool ForceRValue = false); void AddConversionCandidate(CXXConversionDecl *Conversion, Expr *From, QualType ToType, OverloadCandidateSet& CandidateSet); @@ -1590,11 +1594,12 @@ public: // virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S, const CXXScopeSpec *SS); - + virtual DeclPtrTy ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, ExprTy *BitfieldWidth, - ExprTy *Init); + ExprTy *Init, + bool Deleted = false); virtual MemInitResult ActOnMemInitializer(DeclPtrTy ConstructorD, Scope *S, @@ -2319,7 +2324,9 @@ public: bool IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType); bool PerformImplicitConversion(Expr *&From, QualType ToType, - const char *Flavor, bool AllowExplicit = false); + const char *Flavor, + bool AllowExplicit = false, + bool Elidable = false); bool PerformImplicitConversion(Expr *&From, QualType ToType, const ImplicitConversionSequence& ICS, const char *Flavor); @@ -2415,7 +2422,8 @@ public: bool CheckReferenceInit(Expr *&simpleInit_or_initList, QualType &declType, ImplicitConversionSequence *ICS = 0, bool SuppressUserConversions = false, - bool AllowExplicit = false); + bool AllowExplicit = false, + bool ForceRValue = false); /// CheckCastTypes - Check type constraints for casting between types. bool CheckCastTypes(SourceRange TyRange, QualType CastTy, Expr *&CastExpr); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index feff707e15..6320798d6d 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -473,7 +473,7 @@ void Sema::ActOnBaseSpecifiers(DeclPtrTy ClassDecl, BaseTy **Bases, /// declarators on it. Sema::DeclPtrTy Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, - ExprTy *BW, ExprTy *InitExpr) { + ExprTy *BW, ExprTy *InitExpr, bool Deleted) { const DeclSpec &DS = D.getDeclSpec(); DeclarationName Name = GetNameForDeclarator(D); Expr *BitWidth = static_cast(BW); @@ -591,6 +591,8 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, if (Init) AddInitializerToDecl(DeclPtrTy::make(Member), ExprArg(*this, Init), false); + if (Deleted) // FIXME: Source location is not very good. + SetDeclDeleted(DeclPtrTy::make(Member), D.getSourceRange().getBegin()); if (isInstField) { FieldCollector->Add(cast(Member)); @@ -1981,11 +1983,12 @@ Sema::CompareReferenceRelationship(QualType T1, QualType T2, /// suppressed. /// When @p AllowExplicit, we also permit explicit user-defined /// conversion functions. +/// When @p ForceRValue, we unconditionally treat the initializer as an rvalue. bool Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, ImplicitConversionSequence *ICS, bool SuppressUserConversions, - bool AllowExplicit) { + bool AllowExplicit, bool ForceRValue) { assert(DeclType->isReferenceType() && "Reference init needs a reference"); QualType T1 = DeclType->getAsReferenceType()->getPointeeType(); @@ -2014,7 +2017,8 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, // Compute some basic properties of the types and the initializer. bool isRValRef = DeclType->isRValueReferenceType(); bool DerivedToBase = false; - Expr::isLvalueResult InitLvalue = Init->isLvalue(Context); + Expr::isLvalueResult InitLvalue = ForceRValue ? Expr::LV_InvalidExpression : + Init->isLvalue(Context); ReferenceCompareResult RefRelationship = CompareReferenceRelationship(T1, T2, DerivedToBase); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 4a5bfd59ae..e2a21f842d 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -737,13 +737,23 @@ Sema::IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType) { /// error, false otherwise. The expression From is replaced with the /// converted expression. Flavor is the kind of conversion we're /// performing, used in the error message. If @p AllowExplicit, -/// explicit user-defined conversions are permitted. -bool +/// explicit user-defined conversions are permitted. @p Elidable should be true +/// when called for copies which may be elided (C++ 12.8p15). C++0x overload +/// resolution works differently in that case. +bool Sema::PerformImplicitConversion(Expr *&From, QualType ToType, - const char *Flavor, bool AllowExplicit) + const char *Flavor, bool AllowExplicit, + bool Elidable) { - ImplicitConversionSequence ICS = TryImplicitConversion(From, ToType, false, - AllowExplicit); + ImplicitConversionSequence ICS; + ICS.ConversionKind = ImplicitConversionSequence::BadConversion; + if (Elidable && getLangOptions().CPlusPlus0x) { + ICS = TryImplicitConversion(From, ToType, /*SuppressUserConversions*/false, + AllowExplicit, /*ForceRValue*/true); + } + if (ICS.ConversionKind == ImplicitConversionSequence::BadConversion) { + ICS = TryImplicitConversion(From, ToType, false, AllowExplicit); + } return PerformImplicitConversion(From, ToType, ICS, Flavor); } diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 875a38253d..70c9ee7ef9 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -376,17 +376,20 @@ Sema::IsOverload(FunctionDecl *New, Decl* OldD, /// not permitted. /// If @p AllowExplicit, then explicit user-defined conversions are /// permitted. +/// If @p ForceRValue, then overloading is performed as if From was an rvalue, +/// no matter its actual lvalueness. ImplicitConversionSequence Sema::TryImplicitConversion(Expr* From, QualType ToType, bool SuppressUserConversions, - bool AllowExplicit) + bool AllowExplicit, bool ForceRValue) { ImplicitConversionSequence ICS; if (IsStandardConversion(From, ToType, ICS.Standard)) ICS.ConversionKind = ImplicitConversionSequence::StandardConversion; else if (getLangOptions().CPlusPlus && IsUserDefinedConversion(From, ToType, ICS.UserDefined, - !SuppressUserConversions, AllowExplicit)) { + !SuppressUserConversions, AllowExplicit, + ForceRValue)) { ICS.ConversionKind = ImplicitConversionSequence::UserDefinedConversion; // C++ [over.ics.user]p4: // A conversion of an expression of class type to the same class @@ -1313,10 +1316,13 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType) /// \param AllowExplicit true if the conversion should consider C++0x /// "explicit" conversion functions as well as non-explicit conversion /// functions (C++0x [class.conv.fct]p2). +/// +/// \param ForceRValue true if the expression should be treated as an rvalue +/// for overload resolution. bool Sema::IsUserDefinedConversion(Expr *From, QualType ToType, UserDefinedConversionSequence& User, bool AllowConversionFunctions, - bool AllowExplicit) + bool AllowExplicit, bool ForceRValue) { OverloadCandidateSet CandidateSet; if (const RecordType *ToRecordType = ToType->getAsRecordType()) { @@ -1340,7 +1346,7 @@ bool Sema::IsUserDefinedConversion(Expr *From, QualType ToType, CXXConstructorDecl *Constructor = cast(*Con); if (Constructor->isConvertingConstructor()) AddOverloadCandidate(Constructor, &From, 1, CandidateSet, - /*SuppressUserConversions=*/true); + /*SuppressUserConversions=*/true, ForceRValue); } } } @@ -1856,24 +1862,29 @@ Sema::CompareDerivedToBaseConversions(const StandardConversionSequence& SCS1, /// sequence required to pass this argument, which may be a bad /// conversion sequence (meaning that the argument cannot be passed to /// a parameter of this type). If @p SuppressUserConversions, then we -/// do not permit any user-defined conversion sequences. +/// do not permit any user-defined conversion sequences. If @p ForceRValue, +/// then we treat @p From as an rvalue, even if it is an lvalue. ImplicitConversionSequence Sema::TryCopyInitialization(Expr *From, QualType ToType, - bool SuppressUserConversions) { + bool SuppressUserConversions, bool ForceRValue) { if (ToType->isReferenceType()) { ImplicitConversionSequence ICS; - CheckReferenceInit(From, ToType, &ICS, SuppressUserConversions); + CheckReferenceInit(From, ToType, &ICS, SuppressUserConversions, + /*AllowExplicit=*/false, ForceRValue); return ICS; } else { - return TryImplicitConversion(From, ToType, SuppressUserConversions); + return TryImplicitConversion(From, ToType, SuppressUserConversions, + ForceRValue); } } -/// PerformArgumentPassing - Pass the argument Arg into a parameter of -/// type ToType. Returns true (and emits a diagnostic) if there was -/// an error, returns false if the initialization succeeded. +/// PerformCopyInitialization - Copy-initialize an object of type @p ToType with +/// the expression @p From. Returns true (and emits a diagnostic) if there was +/// an error, returns false if the initialization succeeded. Elidable should +/// be true when the copy may be elided (C++ 12.8p15). Overload resolution works +/// differently in C++0x for this case. bool Sema::PerformCopyInitialization(Expr *&From, QualType ToType, - const char* Flavor) { + const char* Flavor, bool Elidable) { if (!getLangOptions().CPlusPlus) { // In C, argument passing is the same as performing an assignment. QualType FromType = From->getType(); @@ -1883,13 +1894,14 @@ bool Sema::PerformCopyInitialization(Expr *&From, QualType ToType, return DiagnoseAssignmentResult(ConvTy, From->getLocStart(), ToType, FromType, From, Flavor); } - + if (ToType->isReferenceType()) return CheckReferenceInit(From, ToType); - if (!PerformImplicitConversion(From, ToType, Flavor)) + if (!PerformImplicitConversion(From, ToType, Flavor, + /*AllowExplicit=*/false, Elidable)) return false; - + return Diag(From->getSourceRange().getBegin(), diag::err_typecheck_convert_incompatible) << ToType << From->getType() << Flavor << From->getSourceRange(); @@ -1997,11 +2009,15 @@ bool Sema::PerformContextuallyConvertToBool(Expr *&From) { /// candidate functions, using the given function call arguments. If /// @p SuppressUserConversions, then don't allow user-defined /// conversions via constructors or conversion operators. +/// If @p ForceRValue, treat all arguments as rvalues. This is a slightly +/// hacky way to implement the overloading rules for elidable copy +/// initialization in C++0x (C++0x 12.8p15). void Sema::AddOverloadCandidate(FunctionDecl *Function, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, - bool SuppressUserConversions) + bool SuppressUserConversions, + bool ForceRValue) { const FunctionProtoType* Proto = dyn_cast(Function->getType()->getAsFunctionType()); @@ -2017,7 +2033,7 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, // function, e.g., X::f(). We use a NULL object as the implied // object argument (C++ [over.call.func]p3). AddMethodCandidate(Method, 0, Args, NumArgs, CandidateSet, - SuppressUserConversions); + SuppressUserConversions, ForceRValue); return; } @@ -2064,7 +2080,7 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, QualType ParamType = Proto->getArgType(ArgIdx); Candidate.Conversions[ArgIdx] = TryCopyInitialization(Args[ArgIdx], ParamType, - SuppressUserConversions); + SuppressUserConversions, ForceRValue); if (Candidate.Conversions[ArgIdx].ConversionKind == ImplicitConversionSequence::BadConversion) { Candidate.Viable = false; @@ -2099,12 +2115,14 @@ void Sema::AddFunctionCandidates(const FunctionSet &Functions, /// @c o.f(a1,a2), @c Object will contain @c o and @c Args will contain /// both @c a1 and @c a2. If @p SuppressUserConversions, then don't /// allow user-defined conversions via constructors or conversion -/// operators. +/// operators. If @p ForceRValue, treat all arguments as rvalues. This is +/// a slightly hacky way to implement the overloading rules for elidable copy +/// initialization in C++0x (C++0x 12.8p15). void Sema::AddMethodCandidate(CXXMethodDecl *Method, Expr *Object, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, - bool SuppressUserConversions) + bool SuppressUserConversions, bool ForceRValue) { const FunctionProtoType* Proto = dyn_cast(Method->getType()->getAsFunctionType()); @@ -2169,7 +2187,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, Expr *Object, QualType ParamType = Proto->getArgType(ArgIdx); Candidate.Conversions[ArgIdx + 1] = TryCopyInitialization(Args[ArgIdx], ParamType, - SuppressUserConversions); + SuppressUserConversions, ForceRValue); if (Candidate.Conversions[ArgIdx + 1].ConversionKind == ImplicitConversionSequence::BadConversion) { Candidate.Viable = false; diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index d258ec3869..d2e158658c 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -754,6 +754,32 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp)); } +/// IsReturnCopyElidable - Whether returning @p RetExpr from a function that +/// returns a @p RetType fulfills the criteria for copy elision (C++0x 12.8p15). +static bool IsReturnCopyElidable(ASTContext &Ctx, QualType RetType, + Expr *RetExpr) { + QualType ExprType = RetExpr->getType(); + // - in a return statement in a function with ... + // ... a class return type ... + if (!RetType->isRecordType()) + return false; + // ... the same cv-unqualified type as the function return type ... + if (Ctx.getCanonicalType(RetType).getUnqualifiedType() != + Ctx.getCanonicalType(ExprType).getUnqualifiedType()) + return false; + // ... the expression is the name of a non-volatile automatic object ... + // We ignore parentheses here. + // FIXME: Is this compliant? + const DeclRefExpr *DR = dyn_cast(RetExpr->IgnoreParens()); + if (!DR) + return false; + const VarDecl *VD = dyn_cast(DR->getDecl()); + if (!VD) + return false; + return VD->hasLocalStorage() && !VD->getType()->isReferenceType() + && !VD->getType().isVolatileQualified(); +} + Action::OwningStmtResult Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) { Expr *RetValExp = static_cast(rex.release()); @@ -800,16 +826,31 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) { if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) { // we have a non-void function with an expression, continue checking - QualType RetValType = RetValExp->getType(); // C99 6.8.6.4p3(136): The return statement is not an assignment. The // overlap restriction of subclause 6.5.16.1 does not apply to the case of // function return. + // C++0x 12.8p15: When certain criteria are met, an implementation is + // allowed to omit the copy construction of a class object, [...] + // - in a return statement in a function with a class return type, when + // the expression is the name of a non-volatile automatic object with + // the same cv-unqualified type as the function return type, the copy + // operation can be omitted [...] + // C++0x 12.8p16: When the criteria for elision of a copy operation are met + // and the object to be copied is designated by an lvalue, overload + // resolution to select the constructor for the copy is first performed + // as if the object were designated by an rvalue. + // Note that we only compute Elidable if we're in C++0x, since we don't + // care otherwise. + bool Elidable = getLangOptions().CPlusPlus0x ? + IsReturnCopyElidable(Context, FnRetType, RetValExp) : + false; + // In C++ the return statement is handled via a copy initialization. // the C version of which boils down to CheckSingleAssignmentConstraints. - // FIXME: Leaks RetValExp. - if (PerformCopyInitialization(RetValExp, FnRetType, "returning")) + // FIXME: Leaks RetValExp on error. + if (PerformCopyInitialization(RetValExp, FnRetType, "returning", Elidable)) return StmtError(); if (RetValExp) CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc); diff --git a/test/SemaCXX/deleted-function.cpp b/test/SemaCXX/deleted-function.cpp index 59a8260334..0e36396023 100644 --- a/test/SemaCXX/deleted-function.cpp +++ b/test/SemaCXX/deleted-function.cpp @@ -15,8 +15,20 @@ void fn3() { void ov(int) {} // expected-note {{candidate function}} void ov(double) = delete; // expected-note {{candidate function has been explicitly deleted}} +struct WithDel { + WithDel() = delete; // expected-note {{candidate function has been explicitly deleted}} + void fn() = delete; // expected-note {{function has been explicitly marked deleted here}} + operator int() = delete; + void operator +(int) = delete; +}; + void test() { fn(); // expected-error {{call to deleted function 'fn'}} ov(1); ov(1.0); // expected-error {{call to deleted function 'ov'}} + + WithDel dd; // expected-error {{call to deleted constructor of 'dd'}} + WithDel *d = 0; + d->fn(); // expected-error {{attempt to use a deleted function}} + int i = *d; // expected-error {{incompatible type initializing}} } diff --git a/test/SemaCXX/rval-references.cpp b/test/SemaCXX/rval-references.cpp index 569693a49b..a7d26bb4b0 100644 --- a/test/SemaCXX/rval-references.cpp +++ b/test/SemaCXX/rval-references.cpp @@ -52,3 +52,40 @@ void f() { } catch(int&&) { // expected-error {{cannot catch exceptions by rvalue reference}} } } + +int&& should_warn(int i) { + // FIXME: The stack address return test doesn't reason about casts. + return static_cast(i); // xpected-warning {{returning reference to temporary}} +} +int&& should_not_warn(int&& i) { // But GCC 4.4 does + return static_cast(i); +} + + +// Test the return dance. This also tests IsReturnCopyElidable. +struct MoveOnly { + MoveOnly(); + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&); + MoveOnly(int&&); +}; + +MoveOnly returning() { + MoveOnly mo; + return mo; +} + +MoveOnly gmo; +MoveOnly returningNonEligible() { + int i; + static MoveOnly mo; + MoveOnly &r = mo; + if (0) // Copy from global can't be elided + return gmo; // expected-error {{incompatible type returning}} + else if (0) // Copy from local static can't be elided + return mo; // expected-error {{incompatible type returning}} + else if (0) // Copy from reference can't be elided + return r; // expected-error {{incompatible type returning}} + else // Construction from different type can't be elided + return i; // expected-error {{incompatible type returning}} +} -- 2.40.0