From c938c1668b4fd12af154e965dd935a89e4801a70 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 26 Jan 2011 05:01:58 +0000 Subject: [PATCH] Rvalue references for *this: - Add ref-qualifiers to the type system; they are part of the canonical type. Print & profile ref-qualifiers - Translate the ref-qualifier from the Declarator chunk for functions to the function type. - Diagnose mis-uses of ref-qualifiers w.r.t. static member functions, free functions, constructors, destructors, etc. - Add serialization and deserialization of ref-qualifiers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124281 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Type.h | 38 ++++++++++++-- include/clang/Basic/DiagnosticSemaKinds.td | 11 ++++ include/clang/Sema/Sema.h | 3 +- lib/AST/Type.cpp | 4 +- lib/AST/TypePrinter.cpp | 17 ++++++- lib/Sema/SemaDeclCXX.cpp | 22 +++++++- lib/Sema/SemaTemplateDeduction.cpp | 1 + lib/Sema/SemaType.cpp | 50 +++++++++++++++---- lib/Sema/TreeTransform.h | 7 ++- lib/Serialization/ASTReader.cpp | 1 + lib/Serialization/ASTWriter.cpp | 1 + .../dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp | 26 ++++++++++ test/CXX/special/class.ctor/p4-0x.cpp | 7 +++ test/CXX/special/class.dtor/p2-0x.cpp | 10 ++++ 14 files changed, 177 insertions(+), 21 deletions(-) create mode 100644 test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp create mode 100644 test/CXX/special/class.ctor/p4-0x.cpp create mode 100644 test/CXX/special/class.dtor/p2-0x.cpp diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 95c5797e5b..cc365a2f87 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -823,6 +823,18 @@ public: } }; +/// \brief The kind of C++0x ref-qualifier associated with a function type, +/// which determines whether a member function's "this" object can be an +/// lvalue, rvalue, or neither. +enum RefQualifierKind { + /// \brief No ref-qualifier was provided. + RQ_None = 0, + /// \brief An lvalue ref-qualifier was provided (\c &). + RQ_LValue, + /// \brief An rvalue ref-qualifier was provided (\c &&). + RQ_RValue +}; + /// Type - This is the base class of the type hierarchy. A central concept /// with types is that each type always has a canonical type. A canonical type /// is the type with any typedef names stripped out of it or the types it @@ -961,6 +973,11 @@ protected: /// C++ 8.3.5p4: The return type, the parameter type list and the /// cv-qualifier-seq, [...], are part of the function type. unsigned TypeQuals : 3; + + /// \brief The ref-qualifier associated with a \c FunctionProtoType. + /// + /// This is a value of type \c RefQualifierKind. + unsigned RefQualifier : 2; }; class ObjCObjectTypeBitfields { @@ -2267,7 +2284,8 @@ class FunctionType : public Type { protected: FunctionType(TypeClass tc, QualType res, bool variadic, - unsigned typeQuals, QualType Canonical, bool Dependent, + unsigned typeQuals, RefQualifierKind RefQualifier, + QualType Canonical, bool Dependent, bool VariablyModified, bool ContainsUnexpandedParameterPack, ExtInfo Info) : Type(tc, Canonical, Dependent, VariablyModified, @@ -2276,9 +2294,15 @@ protected: FunctionTypeBits.ExtInfo = Info.Bits; FunctionTypeBits.Variadic = variadic; FunctionTypeBits.TypeQuals = typeQuals; + FunctionTypeBits.RefQualifier = static_cast(RefQualifier); } bool isVariadic() const { return FunctionTypeBits.Variadic; } unsigned getTypeQuals() const { return FunctionTypeBits.TypeQuals; } + + RefQualifierKind getRefQualifier() const { + return static_cast(FunctionTypeBits.RefQualifier); + } + public: QualType getResultType() const { return ResultType; } @@ -2307,7 +2331,7 @@ public: /// no information available about its arguments. class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { FunctionNoProtoType(QualType Result, QualType Canonical, ExtInfo Info) - : FunctionType(FunctionNoProto, Result, false, 0, Canonical, + : FunctionType(FunctionNoProto, Result, false, 0, RQ_None, Canonical, /*Dependent=*/false, Result->isVariablyModifiedType(), /*ContainsUnexpandedParameterPack=*/false, Info) {} @@ -2345,13 +2369,14 @@ public: struct ExtProtoInfo { ExtProtoInfo() : Variadic(false), HasExceptionSpec(false), HasAnyExceptionSpec(false), - TypeQuals(0), NumExceptions(0), Exceptions(0) {} + TypeQuals(0), RefQualifier(RQ_None), NumExceptions(0), Exceptions(0) {} FunctionType::ExtInfo ExtInfo; bool Variadic; bool HasExceptionSpec; bool HasAnyExceptionSpec; unsigned char TypeQuals; + RefQualifierKind RefQualifier; unsigned NumExceptions; const QualType *Exceptions; }; @@ -2405,6 +2430,7 @@ public: EPI.HasExceptionSpec = hasExceptionSpec(); EPI.HasAnyExceptionSpec = hasAnyExceptionSpec(); EPI.TypeQuals = static_cast(getTypeQuals()); + EPI.RefQualifier = getRefQualifier(); EPI.NumExceptions = NumExceptions; EPI.Exceptions = exception_begin(); return EPI; @@ -2434,6 +2460,12 @@ public: unsigned getTypeQuals() const { return FunctionType::getTypeQuals(); } + + /// \brief Retrieve the ref-qualifier associated with this function type. + RefQualifierKind getRefQualifier() const { + return FunctionType::getRefQualifier(); + } + typedef const QualType *arg_type_iterator; arg_type_iterator arg_type_begin() const { return reinterpret_cast(this+1); diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 55e9ca3133..fe4c182cfe 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -730,6 +730,9 @@ def err_covariant_return_type_class_type_more_qualified : Error< def err_constructor_cannot_be : Error<"constructor cannot be declared '%0'">; def err_invalid_qualified_constructor : Error< "'%0' qualifier is not allowed on a constructor">; +def err_ref_qualifier_constructor : Error< + "ref-qualifier '%select{&&|&}0' is not allowed on a constructor">; + def err_constructor_return_type : Error< "constructor cannot have a return type">; def err_constructor_redeclared : Error<"constructor cannot be redeclared">; @@ -749,6 +752,8 @@ def err_destructor_not_member : Error< def err_destructor_cannot_be : Error<"destructor cannot be declared '%0'">; def err_invalid_qualified_destructor : Error< "'%0' qualifier is not allowed on a destructor">; +def err_ref_qualifier_destructor : Error< + "ref-qualifier '%select{&&|&}0' is not allowed on a destructor">; def err_destructor_return_type : Error<"destructor cannot have a return type">; def err_destructor_redeclared : Error<"destructor cannot be redeclared">; def err_destructor_with_params : Error<"destructor cannot have any parameters">; @@ -2461,11 +2466,17 @@ def err_invalid_member_use_in_static_method : Error< "invalid use of member %0 in static member function">; def err_invalid_qualified_function_type : Error< "type qualifier is not allowed on this function">; +def err_invalid_ref_qualifier_function_type : Error< + "ref-qualifier '%select{&&|&}0' is only allowed on non-static member functions," + " member function pointers, and typedefs of function types">; def err_invalid_qualified_function_pointer : Error< "type qualifier is not allowed on this function %select{pointer|reference}0">; def err_invalid_qualified_typedef_function_type_use : Error< "a qualified function type cannot be used to declare a " "%select{static member|nonmember}0 function">; +def err_invalid_ref_qualifier_typedef_function_type_use : Error< + "%select{static member|nonmember}0 function cannot have a ref-qualifier " + "'%select{&&|&}1'">; def err_invalid_non_static_member_use : Error< "invalid use of nonstatic data member %0">; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 90242e6192..cc6493541b 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -620,7 +620,8 @@ public: SourceLocation AttrLoc); QualType BuildFunctionType(QualType T, QualType *ParamTypes, unsigned NumParamTypes, - bool Variadic, unsigned Quals, + bool Variadic, unsigned Quals, + RefQualifierKind RefQualifier, SourceLocation Loc, DeclarationName Entity, FunctionType::ExtInfo Info); QualType BuildMemberPointerType(QualType T, QualType Class, diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d8939cc123..36d501577c 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1112,7 +1112,8 @@ llvm::StringRef FunctionType::getNameForCallConv(CallingConv CC) { FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, unsigned numArgs, QualType canonical, const ExtProtoInfo &epi) - : FunctionType(FunctionProto, result, epi.Variadic, epi.TypeQuals, canonical, + : FunctionType(FunctionProto, result, epi.Variadic, epi.TypeQuals, + epi.RefQualifier, canonical, result->isDependentType(), result->isVariablyModifiedType(), result->containsUnexpandedParameterPack(), @@ -1162,6 +1163,7 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, ID.AddPointer(ArgTys[i].getAsOpaquePtr()); ID.AddBoolean(epi.Variadic); ID.AddInteger(epi.TypeQuals); + ID.AddInteger(epi.RefQualifier); if (epi.HasExceptionSpec) { ID.AddBoolean(epi.HasAnyExceptionSpec); for (unsigned i = 0; i != epi.NumExceptions; ++i) diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 8a740d4ad8..c0ce1f25bf 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -354,6 +354,21 @@ void TypePrinter::printFunctionProto(const FunctionProtoType *T, S += " __attribute__((regparm (" + llvm::utostr_32(Info.getRegParm()) + ")))"; + AppendTypeQualList(S, T->getTypeQuals()); + + switch (T->getRefQualifier()) { + case RQ_None: + break; + + case RQ_LValue: + S += " &"; + break; + + case RQ_RValue: + S += " &&"; + break; + } + if (T->hasExceptionSpec()) { S += " throw("; if (T->hasAnyExceptionSpec()) @@ -370,8 +385,6 @@ void TypePrinter::printFunctionProto(const FunctionProtoType *T, S += ")"; } - AppendTypeQualList(S, T->getTypeQuals()); - print(T->getResultType(), S); } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index d5dea78230..78d9a5b959 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3023,6 +3023,15 @@ QualType Sema::CheckConstructorDeclarator(Declarator &D, QualType R, D.setInvalidType(); } + // C++0x [class.ctor]p4: + // A constructor shall not be declared with a ref-qualifier. + if (FTI.hasRefQualifier()) { + Diag(FTI.getRefQualifierLoc(), diag::err_ref_qualifier_constructor) + << FTI.RefQualifierIsLValueRef + << FixItHint::CreateRemoval(FTI.getRefQualifierLoc()); + D.setInvalidType(); + } + // Rebuild the function type "R" without any type qualifiers (in // case any of the errors above fired) and with "void" as the // return type, since constructors don't have return types. @@ -3032,7 +3041,8 @@ QualType Sema::CheckConstructorDeclarator(Declarator &D, QualType R, FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo(); EPI.TypeQuals = 0; - + EPI.RefQualifier = RQ_None; + return Context.getFunctionType(Context.VoidTy, Proto->arg_type_begin(), Proto->getNumArgs(), EPI); } @@ -3173,6 +3183,15 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, QualType R, D.setInvalidType(); } + // C++0x [class.dtor]p2: + // A destructor shall not be declared with a ref-qualifier. + if (FTI.hasRefQualifier()) { + Diag(FTI.getRefQualifierLoc(), diag::err_ref_qualifier_destructor) + << FTI.RefQualifierIsLValueRef + << FixItHint::CreateRemoval(FTI.getRefQualifierLoc()); + D.setInvalidType(); + } + // Make sure we don't have any parameters. if (FTI.NumArgs > 0 && !FTIHasSingleVoidArgument(FTI)) { Diag(D.getIdentifierLoc(), diag::err_destructor_with_params); @@ -3199,6 +3218,7 @@ QualType Sema::CheckDestructorDeclarator(Declarator &D, QualType R, FunctionProtoType::ExtProtoInfo EPI = Proto->getExtProtoInfo(); EPI.Variadic = false; EPI.TypeQuals = 0; + EPI.RefQualifier = RQ_None; return Context.getFunctionType(Context.VoidTy, 0, 0, EPI); } diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 00d92bc6b5..70e9973946 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -2106,6 +2106,7 @@ Sema::SubstituteExplicitTemplateArguments( ParamTypes.data(), ParamTypes.size(), Proto->isVariadic(), Proto->getTypeQuals(), + Proto->getRefQualifier(), Function->getLocation(), Function->getDeclName(), Proto->getExtInfo()); diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index b663f58f03..64d5c428f3 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -1269,6 +1269,7 @@ QualType Sema::BuildFunctionType(QualType T, QualType *ParamTypes, unsigned NumParamTypes, bool Variadic, unsigned Quals, + RefQualifierKind RefQualifier, SourceLocation Loc, DeclarationName Entity, FunctionType::ExtInfo Info) { if (T->isArrayType() || T->isFunctionType()) { @@ -1294,6 +1295,7 @@ QualType Sema::BuildFunctionType(QualType T, FunctionProtoType::ExtProtoInfo EPI; EPI.Variadic = Variadic; EPI.TypeQuals = Quals; + EPI.RefQualifier = RefQualifier; EPI.ExtInfo = Info; return Context.getFunctionType(T, ParamTypes, NumParamTypes, EPI); @@ -1722,7 +1724,10 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, FunctionProtoType::ExtProtoInfo EPI; EPI.Variadic = FTI.isVariadic; EPI.TypeQuals = FTI.TypeQuals; - + EPI.RefQualifier = !FTI.hasRefQualifier()? RQ_None + : FTI.RefQualifierIsLValueRef? RQ_LValue + : RQ_RValue; + // Otherwise, we have a function with an argument list that is // potentially variadic. llvm::SmallVector ArgTys; @@ -1876,21 +1881,44 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, FreeFunction = (DC && !DC->isRecord()); } - if (FnTy->getTypeQuals() != 0 && + // C++0x [dcl.fct]p6: + // A ref-qualifier shall only be part of the function type for a + // non-static member function, the function type to which a pointer to + // member refers, or the top-level function type of a function typedef + // declaration. + if ((FnTy->getTypeQuals() != 0 || FnTy->getRefQualifier()) && D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef && (FreeFunction || D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static)) { - if (D.isFunctionDeclarator()) - Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type); - else - Diag(D.getIdentifierLoc(), - diag::err_invalid_qualified_typedef_function_type_use) - << FreeFunction; - - // Strip the cv-quals from the type. + if (FnTy->getTypeQuals() != 0) { + if (D.isFunctionDeclarator()) + Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type); + else + Diag(D.getIdentifierLoc(), + diag::err_invalid_qualified_typedef_function_type_use) + << FreeFunction; + } + + if (FnTy->getRefQualifier()) { + if (D.isFunctionDeclarator()) { + SourceLocation Loc + = D.getTypeObject(D.getNumTypeObjects()-1).Fun.getRefQualifierLoc(); + Diag(Loc, diag::err_invalid_ref_qualifier_function_type) + << (FnTy->getRefQualifier() == RQ_LValue) + << FixItHint::CreateRemoval(Loc); + } else { + Diag(D.getIdentifierLoc(), + diag::err_invalid_ref_qualifier_typedef_function_type_use) + << FreeFunction + << (FnTy->getRefQualifier() == RQ_LValue); + } + } + + // Strip the cv-quals and ref-qualifier from the type. FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo(); EPI.TypeQuals = 0; - + EPI.RefQualifier = RQ_None; + T = Context.getFunctionType(FnTy->getResultType(), FnTy->arg_type_begin(), FnTy->getNumArgs(), EPI); } diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 04c45b8da7..3f8d3e5ec4 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -633,6 +633,7 @@ public: QualType *ParamTypes, unsigned NumParamTypes, bool Variadic, unsigned Quals, + RefQualifierKind RefQualifier, const FunctionType::ExtInfo &Info); /// \brief Build a new unprototyped function type. @@ -3796,6 +3797,7 @@ TreeTransform::TransformFunctionProtoType(TypeLocBuilder &TLB, ParamTypes.size(), T->isVariadic(), T->getTypeQuals(), + T->getRefQualifier(), T->getExtInfo()); if (Result.isNull()) return QualType(); @@ -7178,7 +7180,7 @@ TreeTransform::TransformBlockExpr(BlockExpr *E) { ParamTypes.data(), ParamTypes.size(), BD->isVariadic(), - 0, + 0, RQ_None, BExprFunctionType->getExtInfo()); CurBlock->FunctionType = FunctionType; @@ -7373,9 +7375,10 @@ QualType TreeTransform::RebuildFunctionProtoType(QualType T, unsigned NumParamTypes, bool Variadic, unsigned Quals, + RefQualifierKind RefQualifier, const FunctionType::ExtInfo &Info) { return SemaRef.BuildFunctionType(T, ParamTypes, NumParamTypes, Variadic, - Quals, + Quals, RefQualifier, getDerived().getBaseLocation(), getDerived().getBaseEntity(), Info); diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index a722df707e..b569f4b621 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -2873,6 +2873,7 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) { EPI.Variadic = Record[Idx++]; EPI.TypeQuals = Record[Idx++]; + EPI.RefQualifier = static_cast(Record[Idx++]); EPI.HasExceptionSpec = Record[Idx++]; EPI.HasAnyExceptionSpec = Record[Idx++]; EPI.NumExceptions = Record[Idx++]; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 1e296e8514..e90640711f 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -176,6 +176,7 @@ void ASTTypeWriter::VisitFunctionProtoType(const FunctionProtoType *T) { Writer.AddTypeRef(T->getArgType(I), Record); Record.push_back(T->isVariadic()); Record.push_back(T->getTypeQuals()); + Record.push_back(static_cast(T->getRefQualifier())); Record.push_back(T->hasExceptionSpec()); Record.push_back(T->hasAnyExceptionSpec()); Record.push_back(T->getNumExceptions()); diff --git a/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp new file mode 100644 index 0000000000..a2533ca2cb --- /dev/null +++ b/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +void f0() &; // expected-error{{ref-qualifier '&' is only allowed on non-static member functions, member function pointers, and typedefs of function types}} +void f1() &&; // expected-error{{ref-qualifier '&&' is only allowed on non-static member functions, member function pointers, and typedefs of function types}} + +struct X { + void f0() &; + void f1() &&; + static void f2() &; // expected-error{{ref-qualifier '&' is only allowed on non-static member functions, member function pointers, and typedefs of function types}} + static void f3() &&; // expected-error{{ref-qualifier '&&' is only allowed on non-static member functions, member function pointers, and typedefs of function types}} +}; + +typedef void func_type_lvalue() &; +typedef void func_type_rvalue() &&; + +func_type_lvalue f2; // expected-error{{nonmember function cannot have a ref-qualifier '&'}} +func_type_rvalue f3; // expected-error{{nonmember function cannot have a ref-qualifier '&&'}} + +struct Y { + func_type_lvalue f0; + func_type_rvalue f1; +}; + +void (X::*mpf1)() & = &X::f0; +void (X::*mpf2)() && = &X::f1; + diff --git a/test/CXX/special/class.ctor/p4-0x.cpp b/test/CXX/special/class.ctor/p4-0x.cpp new file mode 100644 index 0000000000..e3508e2e8c --- /dev/null +++ b/test/CXX/special/class.ctor/p4-0x.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +// A constructor shall not be declared with a ref-qualifier. +struct X { + X() &; // expected-error{{ref-qualifier '&' is not allowed on a constructor}} + X(int) &&; // expected-error{{ref-qualifier '&&' is not allowed on a constructor}} +}; diff --git a/test/CXX/special/class.dtor/p2-0x.cpp b/test/CXX/special/class.dtor/p2-0x.cpp new file mode 100644 index 0000000000..53a2e033ef --- /dev/null +++ b/test/CXX/special/class.dtor/p2-0x.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +// A destructor shall not be declared with a ref-qualifier. +struct X { + ~X() &; // expected-error{{ref-qualifier '&' is not allowed on a destructor}} +}; + +struct Y { + ~Y() &&; // expected-error{{ref-qualifier '&&' is not allowed on a destructor}} +}; -- 2.40.0