From 2c9a03f3b249e4d9d76eadf758a33142adc4d0a4 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 26 Jan 2011 19:30:28 +0000 Subject: [PATCH] Rvalue references for *this: implement the implicit conversion rules for the implicit object argument to a non-static member function with a ref-qualifier (C++0x [over.match.funcs]p4). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@124311 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/Expr.h | 6 + include/clang/Sema/Overload.h | 4 +- include/clang/Sema/Sema.h | 7 +- lib/Sema/SemaOverload.cpp | 147 +++++++++++++----- .../over.match/over.match.funcs/p4-0x.cpp | 65 ++++++++ 5 files changed, 183 insertions(+), 46 deletions(-) create mode 100644 test/CXX/over/over.match/over.match.funcs/p4-0x.cpp diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 46fda21fc0..941a982fe3 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -268,6 +268,12 @@ public: bool isPRValue() const { return Kind >= CL_Function; } bool isRValue() const { return Kind >= CL_XValue; } bool isModifiable() const { return getModifiable() == CM_Modifiable; } + + /// \brief Create a simple, modifiably lvalue + static Classification makeSimpleLValue() { + return Classification(CL_LValue, CM_Modifiable); + } + }; /// \brief Classify - Classify this expression according to the C++0x /// expression taxonomy. diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h index 28fa561b1b..3d618689b2 100644 --- a/include/clang/Sema/Overload.h +++ b/include/clang/Sema/Overload.h @@ -294,7 +294,9 @@ namespace clang { no_conversion, unrelated_class, suppressed_user, - bad_qualifiers + bad_qualifiers, + lvalue_ref_to_rvalue, + rvalue_ref_to_lvalue }; // This can be null, e.g. for implicit object arguments. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index cc6493541b..7d51fc34bc 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -20,7 +20,7 @@ #include "clang/Sema/IdentifierResolver.h" #include "clang/Sema/ObjCMethodList.h" #include "clang/Sema/DeclSpec.h" -#include "clang/AST/OperationKinds.h" +#include "clang/AST/Expr.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/ExternalASTSource.h" #include "clang/AST/TypeLoc.h" @@ -1080,12 +1080,14 @@ public: bool SuppressUserConversions = false); void AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType, + Expr::Classification ObjectClassification, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversion = false); void AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, QualType ObjectType, + Expr::Classification ObjectClassification, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions = false); @@ -1094,6 +1096,7 @@ public: CXXRecordDecl *ActingContext, const TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType, + Expr::Classification ObjectClassification, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions = false); @@ -1117,7 +1120,7 @@ public: DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, const FunctionProtoType *Proto, - QualType ObjectTy, Expr **Args, unsigned NumArgs, + Expr *Object, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet); void AddMemberOperatorCandidates(OverloadedOperatorKind Op, SourceLocation OpLoc, diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 6c25893758..62af5bb8ff 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -2345,9 +2345,10 @@ static bool isBetterReferenceBindingKind(const StandardConversionSequence &SCS1, // and std::reference_wrapper when dealing with references to functions. // Proposed wording changes submitted to CWG for consideration. // - // FIXME: Rvalue references. We don't know if we're dealing with the - // implicit object parameter, or if the member function in this case has a - // ref qualifier. (Of course, we don't have ref qualifiers yet.) + // Note: neither of these conditions will evalute true for the implicit + // object parameter, because we don't set either BindsToRvalue or + // BindsToFunctionLvalue when computing the conversion sequence for the + // implicit object parameter. return (!SCS1.IsLvalueReference && SCS1.BindsToRvalue && SCS2.IsLvalueReference) || (SCS1.IsLvalueReference && SCS1.BindsToFunctionLvalue && @@ -3182,6 +3183,7 @@ TryCopyInitialization(Sema &S, Expr *From, QualType ToType, /// expression @p From. static ImplicitConversionSequence TryObjectArgumentInitialization(Sema &S, QualType OrigFromType, + Expr::Classification FromClassification, CXXMethodDecl *Method, CXXRecordDecl *ActingContext) { QualType ClassType = S.Context.getTypeDeclType(ActingContext); @@ -3197,22 +3199,35 @@ TryObjectArgumentInitialization(Sema &S, QualType OrigFromType, // We need to have an object of class type. QualType FromType = OrigFromType; - if (const PointerType *PT = FromType->getAs()) + if (const PointerType *PT = FromType->getAs()) { FromType = PT->getPointeeType(); + // When we had a pointer, it's implicitly dereferenced, so we + // better have an lvalue. + assert(FromClassification.isLValue()); + } + assert(FromType->isRecordType()); - // The implicit object parameter is has the type "reference to cv X", - // where X is the class of which the function is a member - // (C++ [over.match.funcs]p4). However, when finding an implicit - // conversion sequence for the argument, we are not allowed to - // create temporaries or perform user-defined conversions + // C++0x [over.match.funcs]p4: + // For non-static member functions, the type of the implicit object + // parameter is + // + // — "lvalue reference to cv X" for functions declared without a + // ref-qualifier or with the & ref-qualifier + // — "rvalue reference to cv X" for functions declared with the && + // ref-qualifier + // + // where X is the class of which the function is a member and cv is the + // cv-qualification on the member function declaration. + // + // However, when finding an implicit conversion sequence for the argument, we + // are not allowed to create temporaries or perform user-defined conversions // (C++ [over.match.funcs]p5). We perform a simplified version of // reference binding here, that allows class rvalues to bind to // non-constant references. - // First check the qualifiers. We don't care about lvalue-vs-rvalue - // with the implicit object parameter (C++ [over.match.funcs]p5). + // First check the qualifiers. QualType FromTypeCanon = S.Context.getCanonicalType(FromType); if (ImplicitParamType.getCVRQualifiers() != FromTypeCanon.getLocalCVRQualifiers() && @@ -3236,6 +3251,31 @@ TryObjectArgumentInitialization(Sema &S, QualType OrigFromType, return ICS; } + // Check the ref-qualifier. + switch (Method->getRefQualifier()) { + case RQ_None: + // Do nothing; we don't care about lvalueness or rvalueness. + break; + + case RQ_LValue: + if (!FromClassification.isLValue() && Quals != Qualifiers::Const) { + // non-const lvalue reference cannot bind to an rvalue + ICS.setBad(BadConversionSequence::lvalue_ref_to_rvalue, FromType, + ImplicitParamType); + return ICS; + } + break; + + case RQ_RValue: + if (!FromClassification.isRValue()) { + // rvalue reference cannot bind to an lvalue + ICS.setBad(BadConversionSequence::rvalue_ref_to_lvalue, FromType, + ImplicitParamType); + return ICS; + } + break; + } + // Success. Mark this as a reference binding. ICS.setStandard(); ICS.Standard.setAsIdentityConversion(); @@ -3244,10 +3284,10 @@ TryObjectArgumentInitialization(Sema &S, QualType OrigFromType, ICS.Standard.setAllToTypes(ImplicitParamType); ICS.Standard.ReferenceBinding = true; ICS.Standard.DirectBinding = true; - - // FIXME: Rvalue references. - ICS.Standard.IsLvalueReference = true; + ICS.Standard.IsLvalueReference = Method->getRefQualifier() != RQ_RValue; ICS.Standard.BindsToFunctionLvalue = false; + + // Note: we intentionally don't set this; see isBetterReferenceBindingKind(). ICS.Standard.BindsToRvalue = false; return ICS; } @@ -3264,19 +3304,22 @@ Sema::PerformObjectArgumentInitialization(Expr *&From, QualType ImplicitParamRecordType = Method->getThisType(Context)->getAs()->getPointeeType(); + Expr::Classification FromClassification; if (const PointerType *PT = From->getType()->getAs()) { FromRecordType = PT->getPointeeType(); DestType = Method->getThisType(Context); + FromClassification = Expr::Classification::makeSimpleLValue(); } else { FromRecordType = From->getType(); DestType = ImplicitParamRecordType; + FromClassification = From->Classify(Context); } // Note that we always use the true parent context when performing // the actual argument initialization. ImplicitConversionSequence ICS - = TryObjectArgumentInitialization(*this, From->getType(), Method, - Method->getParent()); + = TryObjectArgumentInitialization(*this, From->getType(), FromClassification, + Method, Method->getParent()); if (ICS.isBad()) { if (ICS.Bad.Kind == BadConversionSequence::bad_qualifiers) { Qualifiers FromQs = FromRecordType.getQualifiers(); @@ -3549,7 +3592,7 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, = dyn_cast(Function->getType()->getAs()); assert(Proto && "Functions without a prototype cannot be overloaded"); assert(!Function->getDescribedFunctionTemplate() && - "Use AddTemplateOverloadCandidate for function templates"); + "Use AddTemp∫lateOverloadCandidate for function templates"); if (CXXMethodDecl *Method = dyn_cast(Function)) { if (!isa(Method)) { @@ -3561,7 +3604,8 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, // object argument (C++ [over.call.func]p3), and the acting context // is irrelevant. AddMethodCandidate(Method, FoundDecl, Method->getParent(), - QualType(), Args, NumArgs, CandidateSet, + QualType(), Expr::Classification::makeSimpleLValue(), + Args, NumArgs, CandidateSet, SuppressUserConversions); return; } @@ -3662,7 +3706,8 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns, if (isa(FD) && !cast(FD)->isStatic()) AddMethodCandidate(cast(FD), F.getPair(), cast(FD)->getParent(), - Args[0]->getType(), Args + 1, NumArgs - 1, + Args[0]->getType(), Args[0]->Classify(Context), + Args + 1, NumArgs - 1, CandidateSet, SuppressUserConversions); else AddOverloadCandidate(FD, F.getPair(), Args, NumArgs, CandidateSet, @@ -3674,7 +3719,9 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns, AddMethodTemplateCandidate(FunTmpl, F.getPair(), cast(FunTmpl->getDeclContext()), /*FIXME: explicit args */ 0, - Args[0]->getType(), Args + 1, NumArgs - 1, + Args[0]->getType(), + Args[0]->Classify(Context), + Args + 1, NumArgs - 1, CandidateSet, SuppressUserConversions); else @@ -3690,6 +3737,7 @@ void Sema::AddFunctionCandidates(const UnresolvedSetImpl &Fns, /// method) as a method candidate to the given overload set. void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType ObjectType, + Expr::Classification ObjectClassification, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions) { @@ -3704,12 +3752,12 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, "Expected a member function template"); AddMethodTemplateCandidate(TD, FoundDecl, ActingContext, /*ExplicitArgs*/ 0, - ObjectType, Args, NumArgs, + ObjectType, ObjectClassification, Args, NumArgs, CandidateSet, SuppressUserConversions); } else { AddMethodCandidate(cast(Decl), FoundDecl, ActingContext, - ObjectType, Args, NumArgs, + ObjectType, ObjectClassification, Args, NumArgs, CandidateSet, SuppressUserConversions); } } @@ -3724,6 +3772,7 @@ void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, void Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, QualType ObjectType, + Expr::Classification ObjectClassification, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions) { @@ -3782,8 +3831,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, // Determine the implicit conversion sequence for the object // parameter. Candidate.Conversions[0] - = TryObjectArgumentInitialization(*this, ObjectType, Method, - ActingContext); + = TryObjectArgumentInitialization(*this, ObjectType, ObjectClassification, + Method, ActingContext); if (Candidate.Conversions[0].isBad()) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_conversion; @@ -3827,6 +3876,7 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl, CXXRecordDecl *ActingContext, const TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType, + Expr::Classification ObjectClassification, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet, bool SuppressUserConversions) { @@ -3867,8 +3917,8 @@ Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl, assert(isa(Specialization) && "Specialization is not a member function?"); AddMethodCandidate(cast(Specialization), FoundDecl, - ActingContext, ObjectType, Args, NumArgs, - CandidateSet, SuppressUserConversions); + ActingContext, ObjectType, ObjectClassification, + Args, NumArgs, CandidateSet, SuppressUserConversions); } /// \brief Add a C++ function template specialization as a candidate @@ -3968,8 +4018,9 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, = cast(ImplicitParamType->getAs()->getDecl()); Candidate.Conversions[0] - = TryObjectArgumentInitialization(*this, From->getType(), Conversion, - ConversionContext); + = TryObjectArgumentInitialization(*this, From->getType(), + From->Classify(Context), + Conversion, ConversionContext); if (Candidate.Conversions[0].isBad()) { Candidate.Viable = false; @@ -4113,7 +4164,7 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, DeclAccessPair FoundDecl, CXXRecordDecl *ActingContext, const FunctionProtoType *Proto, - QualType ObjectType, + Expr *Object, Expr **Args, unsigned NumArgs, OverloadCandidateSet& CandidateSet) { if (!CandidateSet.isNewCandidate(Conversion)) @@ -4136,8 +4187,9 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, // Determine the implicit conversion sequence for the implicit // object parameter. ImplicitConversionSequence ObjectInit - = TryObjectArgumentInitialization(*this, ObjectType, Conversion, - ActingContext); + = TryObjectArgumentInitialization(*this, Object->getType(), + Object->Classify(Context), + Conversion, ActingContext); if (ObjectInit.isBad()) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_conversion; @@ -4249,7 +4301,8 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op, Oper != OperEnd; ++Oper) AddMethodCandidate(Oper.getPair(), Args[0]->getType(), - Args + 1, NumArgs - 1, CandidateSet, + Args[0]->Classify(Context), Args + 1, NumArgs - 1, + CandidateSet, /* SuppressUserConversions = */ false); } } @@ -4901,8 +4954,8 @@ public: // T& operator*(T*); // // C++ [over.built]p7: - // For every function type T, there exist candidate operator - // functions of the form + // For every function type T that does not have cv-qualifiers or a + // ref-qualifier, there exist candidate operator functions of the form // T& operator*(T*); void addUnaryStarPointerOverloads() { for (BuiltinCandidateTypeSet::iterator @@ -4914,6 +4967,10 @@ public: if (!PointeeTy->isObjectType() && !PointeeTy->isFunctionType()) continue; + if (const FunctionProtoType *Proto =PointeeTy->getAs()) + if (Proto->getTypeQuals() || Proto->getRefQualifier()) + continue; + S.AddBuiltinCandidate(S.Context.getLValueReferenceType(PointeeTy), &ParamTy, Args, 1, CandidateSet); } @@ -7950,6 +8007,9 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, Qualifier = UnresExpr->getQualifier(); QualType ObjectType = UnresExpr->getBaseType(); + Expr::Classification ObjectClassification + = UnresExpr->isArrow()? Expr::Classification::makeSimpleLValue() + : UnresExpr->getBase()->Classify(Context); // Add overload candidates OverloadCandidateSet CandidateSet(UnresExpr->getMemberLoc()); @@ -7969,6 +8029,7 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, if (isa(Func)) Func = cast(Func)->getTargetDecl(); + // Microsoft supports direct constructor calls. if (getLangOptions().Microsoft && isa(Func)) { AddOverloadCandidate(cast(Func), I.getPair(), Args, NumArgs, @@ -7980,13 +8041,14 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, continue; AddMethodCandidate(Method, I.getPair(), ActingDC, ObjectType, - Args, NumArgs, - CandidateSet, /*SuppressUserConversions=*/false); + ObjectClassification, + Args, NumArgs, CandidateSet, + /*SuppressUserConversions=*/false); } else { AddMethodTemplateCandidate(cast(Func), I.getPair(), ActingDC, TemplateArgs, - ObjectType, Args, NumArgs, - CandidateSet, + ObjectType, ObjectClassification, + Args, NumArgs, CandidateSet, /*SuppressUsedConversions=*/false); } } @@ -8113,7 +8175,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Object, for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end(); Oper != OperEnd; ++Oper) { AddMethodCandidate(Oper.getPair(), Object->getType(), - Args, NumArgs, CandidateSet, + Object->Classify(Context), Args, NumArgs, CandidateSet, /*SuppressUserConversions=*/ false); } @@ -8158,8 +8220,7 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Object, if (const FunctionProtoType *Proto = ConvType->getAs()) AddSurrogateCandidate(Conv, I.getPair(), ActingContext, Proto, - Object->getType(), Args, NumArgs, - CandidateSet); + Object, Args, NumArgs, CandidateSet); } // Perform overload resolution. @@ -8369,8 +8430,8 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc) { for (LookupResult::iterator Oper = R.begin(), OperEnd = R.end(); Oper != OperEnd; ++Oper) { - AddMethodCandidate(Oper.getPair(), Base->getType(), 0, 0, CandidateSet, - /*SuppressUserConversions=*/false); + AddMethodCandidate(Oper.getPair(), Base->getType(), Base->Classify(Context), + 0, 0, CandidateSet, /*SuppressUserConversions=*/false); } // Perform overload resolution. diff --git a/test/CXX/over/over.match/over.match.funcs/p4-0x.cpp b/test/CXX/over/over.match/over.match.funcs/p4-0x.cpp new file mode 100644 index 0000000000..855895af98 --- /dev/null +++ b/test/CXX/over/over.match/over.match.funcs/p4-0x.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + +template T &lvalue(); +template T &&xvalue(); +template T prvalue(); + +struct X0 { + int &f() &; + float &f() &&; + + template int &ft(T) &; + template float &ft(T) &&; + + typedef int &(*func_int_ref)(); + typedef float &(*func_float_ref)(); + + operator func_int_ref() &; + operator func_float_ref() &&; + + void g(); + + int &operator+(const X0&) &; + float &operator+(const X0&) &&; + + template int &operator+(const T&) &; + template float &operator+(const T&) &&; + + int &h() const&; + float &h() &&; +}; + +void X0::g() { + int &ir1 = f(); + int &ir2 = X0::f(); +} + +void test_ref_qualifier_binding() { + int &ir1 = lvalue().f(); + float &fr1 = xvalue().f(); + float &fr2 = prvalue().f(); + int &ir2 = lvalue().ft(1); + float &fr3 = xvalue().ft(2); + float &fr4 = prvalue().ft(3); +} + +void test_ref_qualifier_binding_with_surrogates() { + int &ir1 = lvalue()(); + float &fr1 = xvalue()(); + float &fr2 = prvalue()(); +} + +void test_ref_qualifier_binding_operators() { + int &ir1 = lvalue() + prvalue(); + float &fr1 = xvalue() + prvalue(); + float &fr2 = prvalue() + prvalue(); + int &ir2 = lvalue() + 1; + float &fr3 = xvalue() + 2; + float &fr4 = prvalue() + 3; +} + +void test_ref_qualifier_overloading() { + int &ir1 = lvalue().h(); + float &fr1 = xvalue().h(); + float &fr2 = prvalue().h(); +} -- 2.40.0