diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index c6150d91ecef..e1318f9aab8c 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -3013,8 +3013,7 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, // qualifier. // This is also the point where rvalue references and lvalue inits no longer // go together. - if ((!isRValRef && !T1.isConstQualified()) || - (isRValRef && InitCategory.isLValue())) + if (!isRValRef && !T1.isConstQualified()) return ICS; // -- If T1 is a function type, then @@ -3073,8 +3072,17 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, !S.RequireCompleteType(DeclLoc, T2, 0) && FindConversionForRefInit(S, ICS, DeclType, DeclLoc, Init, T2, /*AllowRvalues=*/true, - AllowExplicit)) + AllowExplicit)) { + // 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. + if (ICS.isUserDefined() && isRValRef && + ICS.UserDefined.After.First == ICK_Lvalue_To_Rvalue) + ICS.setBad(BadConversionSequence::no_conversion, Init, DeclType); + return ICS; + } } // -- Otherwise, a temporary of type "cv1 T1" is created and @@ -3101,6 +3109,12 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, (T1->isRecordType() || T2->isRecordType())) return ICS; + // 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 && + isRValRef && Init->Classify(S.Context).isLValue()) + return 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 @@ -3123,6 +3137,7 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, ICS.UserDefined.After.ReferenceBinding = true; ICS.UserDefined.After.RRefBinding = isRValRef; } + return ICS; } @@ -4005,6 +4020,16 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, Candidate.FailureKind = ovl_fail_final_conversion_not_exact; } + // C++0x [dcl.init.ref]p5: + // 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. + if (ToType->isRValueReferenceType() && + ICS.Standard.First == ICK_Lvalue_To_Rvalue) { + Candidate.Viable = false; + Candidate.FailureKind = ovl_fail_bad_final_conversion; + } break; case ImplicitConversionSequence::BadConversion: diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index fc480391ea1c..333eb32293ad 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2399,6 +2399,18 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(Sema &S, ParamType = ParamType.getLocalUnqualifiedType(); const ReferenceType *ParamRefType = ParamType->getAs(); if (ParamRefType) { + // [C++0x] If P is an rvalue reference to a cv-unqualified + // template parameter and the argument is an lvalue, the type + // "lvalue reference to A" is used in place of A for type + // deduction. + if (const RValueReferenceType *RValueRef + = dyn_cast(ParamType)) { + if (!RValueRef->getPointeeType().getQualifiers() && + isa(RValueRef->getPointeeType()) && + Arg->Classify(S.Context).isLValue()) + ArgType = S.Context.getLValueReferenceType(ArgType); + } + // [...] If P is a reference type, the type referred to by P is used // for type deduction. ParamType = ParamRefType->getPointeeType(); diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3-0x.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3-0x.cpp new file mode 100644 index 000000000000..87fa907636b7 --- /dev/null +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3-0x.cpp @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s + + +// If P is an rvalue reference to a cv-unqualified template parameter +// and the argument is an lvalue, the type "lvalue reference to A" is +// used in place of A for type deduction. +template struct X { }; + +template X f0(T&&); + +struct Y { }; + +template T prvalue(); +template T&& xvalue(); +template T& lvalue(); + +void test_f0() { + X xi0 = f0(prvalue()); + X xi1 = f0(xvalue()); + X xi2 = f0(lvalue()); + X xy0 = f0(prvalue()); + X xy1 = f0(xvalue()); + X xy2 = f0(lvalue()); +}