/// deduction where the parameter is a function type that can be converted
/// to the argument type.
TDF_AllowCompatibleFunctionType = 0x20,
+
+ /// Within template argument deduction for a conversion function, we are
+ /// matching with an argument type for which the original argument was
+ /// a reference.
+ TDF_ArgWithReferenceType = 0x40,
};
}
if (TDF & TDF_ParamWithReferenceType) {
if (hasInconsistentOrSupersetQualifiersOf(Param, Arg))
return Sema::TDK_NonDeducedMismatch;
+ } else if (TDF & TDF_ArgWithReferenceType) {
+ // C++ [temp.deduct.conv]p4:
+ // If the original A is a reference type, A can be more cv-qualified
+ // than the deduced A
+ if (!Arg.getQualifiers().compatiblyIncludes(Param.getQualifiers()))
+ return Sema::TDK_NonDeducedMismatch;
+
+ // Strip out all extra qualifiers from the argument to figure out the
+ // type we're converting to, prior to the qualification conversion.
+ Qualifiers Quals;
+ Arg = S.Context.getUnqualifiedArrayType(Arg, Quals);
+ Arg = S.Context.getQualifiedType(Arg, Param.getQualifiers());
} else if (!IsPossiblyOpaquelyQualifiedType(Param)) {
if (Param.getCVRQualifiers() != Arg.getCVRQualifiers())
return Sema::TDK_NonDeducedMismatch;
// C++0x [temp.deduct.conv]p4:
// [...] If A is a reference type, the type referred to by A is used
// for type deduction.
- if (const ReferenceType *ARef = A->getAs<ReferenceType>())
- A = ARef->getPointeeType().getUnqualifiedType();
+ if (const ReferenceType *ARef = A->getAs<ReferenceType>()) {
+ A = ARef->getPointeeType();
+ // We work around a defect in the standard here: cv-qualifiers are also
+ // removed from P and A in this case, unless P was a reference type. This
+ // seems to mostly match what other compilers are doing.
+ if (!FromType->getAs<ReferenceType>()) {
+ A = A.getUnqualifiedType();
+ P = P.getUnqualifiedType();
+ }
+
// C++ [temp.deduct.conv]p3:
//
// If A is not a reference type:
- else {
+ } else {
assert(!A->isReferenceType() && "Reference types were handled above");
// - If P is an array type, the pointer type produced by the
// cv-qualified than the deduced A (i.e., the type referred to
// by the reference)
if (ToType->isReferenceType())
- TDF |= TDF_ParamWithReferenceType;
+ TDF |= TDF_ArgWithReferenceType;
// - The deduced A can be another pointer or pointer to member
// type that can be converted to A via a qualification
// conversion.
// RUN: %clang_cc1 -fsyntax-only %s -verify
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only %s -verify
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only %s -verify
struct AnyT {
template<typename T>
// deduce T = 'const double'
const double X::* X::* pm1 = apm; // expected-note {{instantiation of}}
}
+
+namespace non_ptr_ref_cv_qual {
+ template<typename Expected>
+ struct ConvToT {
+ template<typename T> operator T() {
+ using Check = T;
+ using Check = Expected;
+ }
+ };
+ const int test_conv_to_t_1 = ConvToT<int>();
+ // We intentionally deviate from [temp.deduct.conv]p4 here, and also remove
+ // the top-level cv-quaifiers from A *after* removing the reference type, if
+ // P is not also a reference type. This matches what other compilers are
+ // doing, and is necessary to support real-world code.
+ const int &test_conv_to_t_2 = ConvToT<int>();
+
+ // Example code that would be broken by the standard's rule.
+ struct Dest {};
+ Dest d1a((ConvToT<Dest>()));
+ Dest d1b = ConvToT<Dest>();
+ Dest &d2 = (d1a = ConvToT<Dest>());
+
+ template<typename Expected>
+ struct ConvToTRef {
+ template<typename T> operator T&() {
+ using Check = T;
+ using Check = Expected;
+ }
+ };
+ const int test_conv_to_t_ref_1 = ConvToTRef<int>();
+ const int &test_conv_to_t_ref_2 = ConvToTRef<const int>();
+
+ Dest d3a((ConvToTRef<const Dest>())); // initialize the copy ctor parameter with 'const Dest&'
+ Dest d3b = ConvToTRef<Dest>(); // convert to non-const T via [over.match.copy]/1.2
+ Dest &d4 = (d3a = ConvToTRef<const Dest>());
+
+ template<typename Expected>
+ struct ConvToConstT {
+ template<typename T> operator const T() {
+ using Check = T;
+ using Check = Expected;
+ }
+ };
+ const int test_conv_to_const_t_1 = ConvToConstT<int>();
+ const int &test_conv_to_const_t_2 = ConvToConstT<int>();
+
+ template<typename Expected>
+ struct ConvToConstTRef {
+ template<typename T> operator const T&() {
+ using Check = T;
+ using Check = Expected;
+ }
+ };
+ const int test_conv_to_const_t_ref_1 = ConvToConstTRef<int>();
+ const int &test_conv_to_const_t_ref_2 = ConvToConstTRef<int>();
+
+ template <typename T, int N> using Arr = T[N];
+ struct ConvToArr {
+ template <int N>
+ operator Arr<int, N> &() {
+ static_assert(N == 3, "");
+ }
+ };
+ int (&test_conv_to_arr_1)[3] = ConvToArr(); // ok
+ const int (&test_conv_to_arr_2)[3] = ConvToArr(); // ok, with qualification conversion
+}