From f8738782e3322923501c8c185b152f0553a37463 Mon Sep 17 00:00:00 2001 From: Sebastian Redl Date: Fri, 27 Mar 2009 21:36:42 +0000 Subject: [PATCH] Better overload resolution for rvalue references. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@67870 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaDeclCXX.cpp | 60 ++++++++++++++++++++------------ lib/Sema/SemaOverload.cpp | 15 ++++---- lib/Sema/SemaOverload.h | 4 +++ test/SemaCXX/rval-references.cpp | 5 +-- 4 files changed, 53 insertions(+), 31 deletions(-) diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 90983040b5..d4efb085d5 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1970,6 +1970,18 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, // -- If the initializer expression + // Rvalue references cannot bind to lvalues (N2812). + // There is absolutely no situation where they can. In particular, note that + // this is ill-formed, even if B has a user-defined conversion to A&&: + // B b; + // A&& r = b; + if (isRValRef && InitLvalue == Expr::LV_Valid) { + if (!ICS) + Diag(Init->getSourceRange().getBegin(), diag::err_lvalue_to_rvalue_ref) + << Init->getSourceRange(); + return true; + } + bool BindsDirectly = false; // -- is an lvalue (but is not a bit-field), and “cv1 T1” is // reference-compatible with “cv2 T2,” or @@ -1980,14 +1992,6 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, RefRelationship >= Ref_Compatible_With_Added_Qualification) { BindsDirectly = true; - // Rvalue references cannot bind to lvalues (N2812). - if (isRValRef) { - if (!ICS) - Diag(Init->getSourceRange().getBegin(), diag::err_lvalue_to_rvalue_ref) - << Init->getSourceRange(); - return true; - } - if (ICS) { // C++ [over.ics.ref]p1: // When a parameter of reference type binds directly (8.5.3) @@ -2004,6 +2008,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, ICS->Standard.ToTypePtr = T1.getAsOpaquePtr(); ICS->Standard.ReferenceBinding = true; ICS->Standard.DirectBinding = true; + ICS->Standard.RRefBinding = false; // Nothing more to do: the inaccessibility/ambiguity check for // derived-to-base conversions is suppressed when we're @@ -2111,7 +2116,8 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, } // -- Otherwise, the reference shall be to a non-volatile const - // type (i.e., cv1 shall be const), or shall be an rvalue reference. + // type (i.e., cv1 shall be const), or the reference shall be an + // rvalue reference and the initializer expression shall be an rvalue. if (!isRValRef && T1.getCVRQualifiers() != QualType::Const) { if (!ICS) Diag(Init->getSourceRange().getBegin(), @@ -2140,7 +2146,7 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, // shall be callable whether or not the copy is actually // done. // - // Note that C++0x [dcl.ref.init]p5 takes away this implementation + // Note that C++0x [dcl.init.ref]p5 takes away this implementation // freedom, so we will always take the first option and never build // a temporary in this case. FIXME: We will, however, have to check // for the presence of a copy constructor in C++98/03 mode. @@ -2154,7 +2160,8 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, ICS->Standard.FromTypePtr = T2.getAsOpaquePtr(); ICS->Standard.ToTypePtr = T1.getAsOpaquePtr(); ICS->Standard.ReferenceBinding = true; - ICS->Standard.DirectBinding = false; + ICS->Standard.DirectBinding = false; + ICS->Standard.RRefBinding = isRValRef; } else { // FIXME: Binding to a subobject of the rvalue is going to require // more AST annotation than this. @@ -2199,18 +2206,27 @@ Sema::CheckReferenceInit(Expr *&Init, QualType &DeclType, // Actually try to convert the initializer to T1. if (ICS) { - /// C++ [over.ics.ref]p2: - /// - /// When a parameter of reference type is not bound directly to - /// an argument expression, the conversion sequence is the one - /// required to convert the argument expression to the - /// underlying type of the reference according to - /// 13.3.3.1. Conceptually, this conversion sequence corresponds - /// to copy-initializing a temporary of the underlying type with - /// the argument expression. Any difference in top-level - /// cv-qualification is subsumed by the initialization itself - /// and does not constitute a conversion. + // C++ [over.ics.ref]p2: + // + // When a parameter of reference type is not bound directly to + // an argument expression, the conversion sequence is the one + // required to convert the argument expression to the + // underlying type of the reference according to + // 13.3.3.1. Conceptually, this conversion sequence corresponds + // to copy-initializing a temporary of the underlying type with + // the argument expression. Any difference in top-level + // cv-qualification is subsumed by the initialization itself + // and does not constitute a conversion. *ICS = TryImplicitConversion(Init, T1, SuppressUserConversions); + // Of course, that's still a reference binding. + if (ICS->ConversionKind == ImplicitConversionSequence::StandardConversion) { + ICS->Standard.ReferenceBinding = true; + ICS->Standard.RRefBinding = isRValRef; + } else if(ICS->ConversionKind == + ImplicitConversionSequence::UserDefinedConversion) { + ICS->UserDefined.After.ReferenceBinding = true; + ICS->UserDefined.After.RRefBinding = isRValRef; + } return ICS->ConversionKind == ImplicitConversionSequence::BadConversion; } else { return PerformImplicitConversion(Init, T1, "initializing"); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 025a245c6c..08c3ce9e5a 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -1584,18 +1584,17 @@ Sema::CompareStandardConversionSequences(const StandardConversionSequence& SCS1, return QualCK; if (SCS1.ReferenceBinding && SCS2.ReferenceBinding) { - QualType T1 = QualType::getFromOpaquePtr(SCS1.ToTypePtr); - QualType T2 = QualType::getFromOpaquePtr(SCS2.ToTypePtr); // C++0x [over.ics.rank]p3b4: // -- S1 and S2 are reference bindings (8.5.3) and neither refers to an // implicit object parameter of a non-static member function declared // without a ref-qualifier, and S1 binds an rvalue reference to an // rvalue and S2 binds an lvalue reference. - // FIXME: We have far too little information for this check. We don't know - // if the bound object is an rvalue. We don't know if the binding type is - // an rvalue or lvalue reference. 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. + // FIXME: 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.) + if (SCS1.RRefBinding != SCS2.RRefBinding) + return SCS1.RRefBinding ? ImplicitConversionSequence::Better + : ImplicitConversionSequence::Worse; // C++ [over.ics.rank]p3b4: // -- S1 and S2 are reference bindings (8.5.3), and the types to @@ -1603,6 +1602,8 @@ Sema::CompareStandardConversionSequences(const StandardConversionSequence& SCS1, // top-level cv-qualifiers, and the type to which the reference // initialized by S2 refers is more cv-qualified than the type // to which the reference initialized by S1 refers. + QualType T1 = QualType::getFromOpaquePtr(SCS1.ToTypePtr); + QualType T2 = QualType::getFromOpaquePtr(SCS2.ToTypePtr); T1 = Context.getCanonicalType(T1); T2 = Context.getCanonicalType(T2); if (T1.getUnqualifiedType() == T2.getUnqualifiedType()) { diff --git a/lib/Sema/SemaOverload.h b/lib/Sema/SemaOverload.h index 3da8eca1a5..e8fad6f01b 100644 --- a/lib/Sema/SemaOverload.h +++ b/lib/Sema/SemaOverload.h @@ -114,6 +114,10 @@ namespace clang { /// direct binding (C++ [dcl.init.ref]). bool DirectBinding : 1; + /// RRefBinding - True when this is a reference binding of an rvalue + /// reference to an rvalue (C++0x [over.ics.rank]p3b4). + bool RRefBinding : 1; + /// FromType - The type that this conversion is converting /// from. This is an opaque pointer that can be translated into a /// QualType. diff --git a/test/SemaCXX/rval-references.cpp b/test/SemaCXX/rval-references.cpp index d3057602a0..569693a49b 100644 --- a/test/SemaCXX/rval-references.cpp +++ b/test/SemaCXX/rval-references.cpp @@ -37,14 +37,15 @@ void f() { not_int ni2 = over(ret_irr()); int i4 = over2(i1); - // not_int ni3 = over2(0); FIXME: this should be well-formed. + not_int ni3 = over2(0); ilr_c1 vilr1 = i1; ilr_c2 vilr2 = i1; conv_to_not_int_rvalue cnir; - not_int &&ni4 = cnir; + not_int &&ni4 = cnir; // expected-error {{rvalue reference cannot bind to lvalue}} not_int &ni5 = cnir; // expected-error{{non-const lvalue reference to type 'struct not_int' cannot be initialized with a value of type 'struct conv_to_not_int_rvalue'}} + not_int &&ni6 = conv_to_not_int_rvalue(); try {