From: Douglas Gregor Date: Fri, 21 Jan 2011 05:18:22 +0000 (+0000) Subject: Implement the special template argument deduction rule for T&& in a X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2ad746aeb90e86cea7afaf552a02ae3f3b5ec859;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@123966 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index c6150d91ec..e1318f9aab 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/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/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index fc480391ea..333eb32293 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/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/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3-0x.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3-0x.cpp new file mode 100644 index 0000000000..87fa907636 --- /dev/null +++ b/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()); +}