From b2855ad68d93824faf47c09bbef90ba74157f0f4 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Fri, 21 Jan 2011 00:52:42 +0000 Subject: [PATCH] More work to bring reference binding up to the latest C++0x specification. In particular, an rvalue reference can bind to an initializer expression that is an lvalue if the referent type and the initializer expression type are not reference-related. This is a newer formulation to the previous "rvalue references can never bind to lvalues" rule. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123952 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaInit.cpp | 17 +++++++----- .../dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp | 26 ++++++++++++++++++- test/SemaCXX/rval-references.cpp | 2 +- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index e25795de40..59a3936131 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2571,23 +2571,19 @@ static void TryReferenceInitialization(Sema &S, // - Otherwise, the reference shall be an lvalue reference to a // non-volatile const type (i.e., cv1 shall be const), or the reference // shall be an rvalue reference. - if (!((isLValueRef && T1Quals.hasConst() && !T1Quals.hasVolatile()) || - (isRValueRef && InitCategory.isRValue()))) { + if (isLValueRef && !(T1Quals.hasConst() && !T1Quals.hasVolatile())) { if (S.Context.getCanonicalType(T2) == S.Context.OverloadTy) Sequence.SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); else if (ConvOvlResult && !Sequence.getFailedCandidateSet().empty()) Sequence.SetOverloadFailure( InitializationSequence::FK_ReferenceInitOverloadFailed, ConvOvlResult); - else if (isLValueRef) + else Sequence.SetFailed(InitCategory.isLValue() ? (RefRelationship == Sema::Ref_Related ? InitializationSequence::FK_ReferenceInitDropsQualifiers : InitializationSequence::FK_NonConstLValueReferenceBindingToUnrelated) : InitializationSequence::FK_NonConstLValueReferenceBindingToTemporary); - else - Sequence.SetFailed( - InitializationSequence::FK_RValueReferenceBindingToLValue); return; } @@ -2696,6 +2692,15 @@ static void TryReferenceInitialization(Sema &S, return; } + // [...] If T1 is reference-related to T2 and the reference is an rvalue + // reference, the initializer expression shall not be an lvalue. + if (RefRelationship >= Sema::Ref_Related && !isLValueRef && + InitCategory.isLValue()) { + Sequence.SetFailed( + InitializationSequence::FK_RValueReferenceBindingToLValue); + return; + } + Sequence.AddReferenceBindingStep(cv1T1, /*bindingTemporary=*/true); return; } diff --git a/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp b/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp index 4629cb6484..fe67d8a917 100644 --- a/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp +++ b/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p5-0x.cpp @@ -17,7 +17,7 @@ int f(int); template struct ConvertsTo { - operator T(); + operator T(); // expected-note 2{{candidate function}} }; void test_rvalue_refs() { @@ -56,6 +56,13 @@ void test_rvalue_refs() { // function lvalue int (&&function1)(int) = ConvertsTo(); + + // In the second case, if the reference is an rvalue reference and + // the second standard conversion sequence of the user-defined + // conversion sequence includes an lvalue-to-rvalue conversion, the + // program is ill-formed. + int &&int2 = ConvertsTo(); // expected-error{{no viable conversion from 'ConvertsTo' to 'int'}} + int &&int3 = ConvertsTo(); // expected-error{{no viable conversion from 'ConvertsTo' to 'int'}} } class NonCopyable { @@ -81,3 +88,20 @@ void test_direct_binding() { const NonCopyable &nc10 = ConvertsTo(); const NonCopyable &nc11 = ConvertsTo(); } + +namespace std_example { + struct A { }; + struct B : A { } b; + extern B f(); + const A& rca = f(); + A&& rra = f(); + struct X { + operator B(); // expected-note{{candidate function}} + operator int&(); // expected-note{{candidate function}} + } x; + const A& r = x; + int i; + int&& rri = static_cast(i); + B&& rrb = x; + int&& rri2 = X(); // expected-error{{no viable conversion from 'std_example::X' to 'int'}} +} diff --git a/test/SemaCXX/rval-references.cpp b/test/SemaCXX/rval-references.cpp index 2068642c9d..ee659ecbd2 100644 --- a/test/SemaCXX/rval-references.cpp +++ b/test/SemaCXX/rval-references.cpp @@ -47,7 +47,7 @@ void f() { ilr_c2 vilr2 = i1; conv_to_not_int_rvalue cnir; - not_int &&ni4 = cnir; // expected-error {{rvalue reference cannot bind to lvalue}} + not_int &&ni4 = cnir; not_int &ni5 = cnir; // expected-error{{non-const lvalue reference to type 'not_int' cannot bind to a value of unrelated type 'conv_to_not_int_rvalue'}} not_int &&ni6 = conv_to_not_int_rvalue(); -- 2.40.0