1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
| // 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>
operator T();
};
void test_cvqual_ref(AnyT any) {
const int &cir = any;
}
struct AnyThreeLevelPtr {
template<typename T>
operator T***() const {
T x = 0; // expected-note 2{{declared const here}}
x = 0; // expected-error 2{{const-qualified type}}
T ***p;
return p;
}
};
struct X { };
void test_deduce_with_qual(AnyThreeLevelPtr a3) {
int * const * const * const ip1 = a3;
// FIXME: This is wrong; we are supposed to deduce 'T = int' here.
const int * const * const * const ip2 = a3; // expected-note {{instantiation of}}
// This one is correct, though.
const double * * * ip3 = a3; // expected-note {{instantiation of}}
}
struct AnyPtrMem {
template<typename Class, typename T>
operator T Class::*() const
{
// This is correct: we don't need a qualification conversion here, so we
// deduce 'T = const float'.
T x = 0; // expected-note {{declared const here}}
x = 0; // expected-error {{const-qualified type}}
return 0;
}
};
void test_deduce_ptrmem_with_qual(AnyPtrMem apm) {
const float X::* pm = apm; // expected-note {{instantiation of}}
}
struct TwoLevelPtrMem {
template<typename Class1, typename Class2, typename T>
operator T Class1::*Class2::*() const
{
T x = 0; // expected-note 2{{declared const here}}
x = 0; // expected-error 2{{const-qualified type}}
return 0;
}
};
void test_deduce_two_level_ptrmem_with_qual(TwoLevelPtrMem apm) {
// FIXME: This is wrong: we should deduce T = 'float'
const float X::* const X::* pm2 = apm; // expected-note {{instantiation of}}
// This is correct: we don't need a qualification conversion, so we directly
// 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
#if __cplusplus >= 201702L
template<bool Noexcept, typename T, typename ...U> using Function = T(U...) noexcept(Noexcept);
template<bool Noexcept> struct ConvToFunction {
template <typename T, typename ...U> operator Function<Noexcept, T, U...>&(); // expected-note {{candidate}}
};
void (&fn1)(int) noexcept(false) = ConvToFunction<false>();
void (&fn2)(int) noexcept(true) = ConvToFunction<false>(); // expected-error {{no viable}}
void (&fn3)(int) noexcept(false) = ConvToFunction<true>();
void (&fn4)(int) noexcept(true) = ConvToFunction<true>();
struct ConvToFunctionDeducingNoexcept {
template <bool Noexcept, typename T, typename ...U> operator Function<Noexcept, T, U...>&();
};
void (&fn5)(int) noexcept(false) = ConvToFunctionDeducingNoexcept();
void (&fn6)(int) noexcept(true) = ConvToFunctionDeducingNoexcept();
#endif
}
|