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
This commit is contained in:
Sebastian Redl 2009-04-12 17:16:29 +00:00
Родитель 36dc958556
Коммит e2b6833d44
9 изменённых файлов: 193 добавлений и 49 удалений

Просмотреть файл

@ -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();
}

Просмотреть файл

@ -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<DeclPtrTy, 8> 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)) {

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<Expr*>(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<FieldDecl>(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);

Просмотреть файл

@ -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);
}

Просмотреть файл

@ -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<CXXConstructorDecl>(*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<FunctionProtoType>(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<FunctionProtoType>(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;

Просмотреть файл

@ -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<DeclRefExpr>(RetExpr->IgnoreParens());
if (!DR)
return false;
const VarDecl *VD = dyn_cast<VarDecl>(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<Expr *>(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);

Просмотреть файл

@ -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}}
}

Просмотреть файл

@ -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<int&&>(i); // xpected-warning {{returning reference to temporary}}
}
int&& should_not_warn(int&& i) { // But GCC 4.4 does
return static_cast<int&&>(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}}
}