Implement the special template argument deduction rule for T&& in a

call (C++0x [temp.deduct.call]p3).

As part of this, start improving the reference-binding implementation
used in the computation of implicit conversion sequences (for overload
resolution) to reflect C++0x semantics. It still needs more work and
testing, of course.

llvm-svn: 123966
This commit is contained in:
Douglas Gregor 2011-01-21 05:18:22 +00:00
parent f4ca47bda8
commit cba72b1f62
3 changed files with 64 additions and 3 deletions

View File

@ -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,9 +3072,18 @@ 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
// initialized from the initializer expression using the
@ -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:

View File

@ -2399,6 +2399,18 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(Sema &S,
ParamType = ParamType.getLocalUnqualifiedType();
const ReferenceType *ParamRefType = ParamType->getAs<ReferenceType>();
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<RValueReferenceType>(ParamType)) {
if (!RValueRef->getPointeeType().getQualifiers() &&
isa<TemplateTypeParmType>(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();

View File

@ -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<typename T> struct X { };
template<typename T> X<T> f0(T&&);
struct Y { };
template<typename T> T prvalue();
template<typename T> T&& xvalue();
template<typename T> T& lvalue();
void test_f0() {
X<int> xi0 = f0(prvalue<int>());
X<int> xi1 = f0(xvalue<int>());
X<int&> xi2 = f0(lvalue<int>());
X<Y> xy0 = f0(prvalue<Y>());
X<Y> xy1 = f0(xvalue<Y>());
X<Y&> xy2 = f0(lvalue<Y>());
}