304 lines
9.8 KiB
C++
304 lines
9.8 KiB
C++
|
// RUN: %clang_cc1 -std=c++1z -verify %s
|
||
|
|
||
|
using size_t = decltype(sizeof(0));
|
||
|
|
||
|
struct A { int x, y; };
|
||
|
struct B { int x, y; };
|
||
|
|
||
|
void no_tuple_size_1() { auto [x, y] = A(); } // ok, decompose elementwise
|
||
|
|
||
|
namespace std { template<typename T> struct tuple_size; }
|
||
|
void no_tuple_size_2() { auto [x, y] = A(); } // ok, decompose elementwise
|
||
|
|
||
|
struct Bad1 { int a, b; };
|
||
|
template<> struct std::tuple_size<Bad1> {};
|
||
|
void no_tuple_size_3() { auto [x, y] = Bad1(); } // ok, omitting value is valid after DR2386
|
||
|
|
||
|
struct Bad2 {};
|
||
|
template<> struct std::tuple_size<Bad2> { const int value = 5; };
|
||
|
void no_tuple_size_4() { auto [x, y] = Bad2(); } // expected-error {{cannot decompose this type; 'std::tuple_size<Bad2>::value' is not a valid integral constant expression}}
|
||
|
|
||
|
template<> struct std::tuple_size<A> { static const int value = 3; };
|
||
|
template<> struct std::tuple_size<B> { enum { value = 3 }; };
|
||
|
|
||
|
void no_get_1() {
|
||
|
{
|
||
|
auto [a0, a1] = A(); // expected-error {{decomposes into 3 elements}}
|
||
|
auto [b0, b1] = B(); // expected-error {{decomposes into 3 elements}}
|
||
|
}
|
||
|
auto [a0, a1, a2] = A(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit initialization of binding declaration 'a0'}}
|
||
|
}
|
||
|
|
||
|
int get(A);
|
||
|
|
||
|
void no_get_2() {
|
||
|
// FIXME: This diagnostic is not great.
|
||
|
auto [a0, a1, a2] = A(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit initialization of binding declaration 'a0'}}
|
||
|
}
|
||
|
|
||
|
template<int> float &get(A); // expected-note 2 {{no known conversion}}
|
||
|
|
||
|
void no_tuple_element_1() {
|
||
|
auto [a0, a1, a2] = A(); // expected-error-re {{'std::tuple_element<0U{{L*}}, A>::type' does not name a type}} expected-note {{in implicit}}
|
||
|
}
|
||
|
|
||
|
namespace std { template<size_t, typename> struct tuple_element; } // expected-note 2{{here}}
|
||
|
|
||
|
void no_tuple_element_2() {
|
||
|
auto [a0, a1, a2] = A(); // expected-error {{implicit instantiation of undefined template 'std::tuple_element<0, A>'}} expected-note {{in implicit}}
|
||
|
}
|
||
|
|
||
|
template<> struct std::tuple_element<0, A> { typedef float type; };
|
||
|
|
||
|
void no_tuple_element_3() {
|
||
|
auto [a0, a1, a2] = A(); // expected-error {{implicit instantiation of undefined template 'std::tuple_element<1, A>'}} expected-note {{in implicit}}
|
||
|
}
|
||
|
|
||
|
template<> struct std::tuple_element<1, A> { typedef float &type; };
|
||
|
template<> struct std::tuple_element<2, A> { typedef const float &type; };
|
||
|
|
||
|
template<int N> auto get(B) -> int (&)[N + 1]; // expected-note 2 {{no known conversion}}
|
||
|
template<int N> struct std::tuple_element<N, B> { typedef int type[N +1 ]; };
|
||
|
|
||
|
template<typename T> struct std::tuple_size<const T> : std::tuple_size<T> {};
|
||
|
template<size_t N, typename T> struct std::tuple_element<N, const T> {
|
||
|
typedef const typename std::tuple_element<N, T>::type type;
|
||
|
};
|
||
|
|
||
|
void referenced_type() {
|
||
|
auto [a0, a1, a2] = A();
|
||
|
auto [b0, b1, b2] = B();
|
||
|
|
||
|
A a;
|
||
|
B b;
|
||
|
auto &[ar0, ar1, ar2] = a;
|
||
|
auto &[br0, br1, br2] = b;
|
||
|
|
||
|
auto &&[arr0, arr1, arr2] = A();
|
||
|
auto &&[brr0, brr1, brr2] = B();
|
||
|
|
||
|
const auto &[acr0, acr1, acr2] = A();
|
||
|
const auto &[bcr0, bcr1, bcr2] = B();
|
||
|
|
||
|
|
||
|
using Float = float;
|
||
|
using Float = decltype(a0);
|
||
|
using Float = decltype(ar0);
|
||
|
using Float = decltype(arr0);
|
||
|
|
||
|
using ConstFloat = const float;
|
||
|
using ConstFloat = decltype(acr0);
|
||
|
|
||
|
using FloatRef = float&;
|
||
|
using FloatRef = decltype(a1);
|
||
|
using FloatRef = decltype(ar1);
|
||
|
using FloatRef = decltype(arr1);
|
||
|
using FloatRef = decltype(acr1);
|
||
|
|
||
|
using ConstFloatRef = const float&;
|
||
|
using ConstFloatRef = decltype(a2);
|
||
|
using ConstFloatRef = decltype(ar2);
|
||
|
using ConstFloatRef = decltype(arr2);
|
||
|
using ConstFloatRef = decltype(acr2);
|
||
|
|
||
|
|
||
|
using Int1 = int[1];
|
||
|
using Int1 = decltype(b0);
|
||
|
using Int1 = decltype(br0);
|
||
|
using Int1 = decltype(brr0);
|
||
|
|
||
|
using ConstInt1 = const int[1];
|
||
|
using ConstInt1 = decltype(bcr0);
|
||
|
|
||
|
using Int2 = int[2];
|
||
|
using Int2 = decltype(b1);
|
||
|
using Int2 = decltype(br1);
|
||
|
using Int2 = decltype(brr1);
|
||
|
|
||
|
using ConstInt2 = const int[2];
|
||
|
using ConstInt2 = decltype(bcr1);
|
||
|
|
||
|
using Int3 = int[3];
|
||
|
using Int3 = decltype(b2);
|
||
|
using Int3 = decltype(br2);
|
||
|
using Int3 = decltype(brr2);
|
||
|
|
||
|
using ConstInt3 = const int[3];
|
||
|
using ConstInt3 = decltype(bcr2);
|
||
|
}
|
||
|
|
||
|
struct C { template<int> int get() const; };
|
||
|
template<> struct std::tuple_size<C> { static const int value = 1; };
|
||
|
template<> struct std::tuple_element<0, C> { typedef int type; };
|
||
|
|
||
|
int member_get() {
|
||
|
auto [c] = C();
|
||
|
using T = int;
|
||
|
using T = decltype(c);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
constexpr C c = C();
|
||
|
template<const C *p> void dependent_binding_PR40674() {
|
||
|
const auto &[c] = *p;
|
||
|
(void)c;
|
||
|
}
|
||
|
|
||
|
struct D {
|
||
|
// FIXME: Emit a note here explaining why this was ignored.
|
||
|
template<int> struct get {};
|
||
|
};
|
||
|
template<> struct std::tuple_size<D> { static const int value = 1; };
|
||
|
template<> struct std::tuple_element<0, D> { typedef D::get<0> type; };
|
||
|
void member_get_class_template() {
|
||
|
auto [d] = D(); // expected-error {{no matching function for call to 'get'}} expected-note {{in implicit init}}
|
||
|
}
|
||
|
|
||
|
struct E {
|
||
|
// FIXME: Emit a note here explaining why this was ignored.
|
||
|
int get();
|
||
|
};
|
||
|
template<> struct std::tuple_size<E> { static const int value = 1; };
|
||
|
template<> struct std::tuple_element<0, E> { typedef int type; };
|
||
|
void member_get_non_template() {
|
||
|
// FIXME: This diagnostic is not very good.
|
||
|
auto [e] = E(); // expected-error {{no matching function for call to 'get'}} expected-note {{in implicit init}}
|
||
|
}
|
||
|
|
||
|
namespace ADL {
|
||
|
struct X {};
|
||
|
};
|
||
|
template<int> int get(ADL::X);
|
||
|
template<> struct std::tuple_size<ADL::X> { static const int value = 1; };
|
||
|
template<> struct std::tuple_element<0, ADL::X> { typedef int type; };
|
||
|
void adl_only_bad() {
|
||
|
auto [x] = ADL::X(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit init}}
|
||
|
}
|
||
|
|
||
|
template<typename ElemType, typename GetTypeLV, typename GetTypeRV>
|
||
|
struct wrap {
|
||
|
template<size_t> GetTypeLV get() &;
|
||
|
template<size_t> GetTypeRV get() &&;
|
||
|
};
|
||
|
template<typename ET, typename GTL, typename GTR>
|
||
|
struct std::tuple_size<wrap<ET, GTL, GTR>> {
|
||
|
static const int value = 1;
|
||
|
};
|
||
|
template<typename ET, typename GTL, typename GTR>
|
||
|
struct std::tuple_element<0, wrap<ET, GTL, GTR>> {
|
||
|
using type = ET;
|
||
|
};
|
||
|
|
||
|
template<typename T> T &lvalue();
|
||
|
|
||
|
void test_value_category() {
|
||
|
// If the declared variable is an lvalue reference, the operand to get is an
|
||
|
// lvalue. Otherwise it's an xvalue.
|
||
|
{ auto [a] = wrap<int, void, int>(); }
|
||
|
{ auto &[a] = lvalue<wrap<int, int, void>>(); }
|
||
|
{ auto &&[a] = wrap<int, void, int>(); }
|
||
|
// If the initializer (call to get) is an lvalue, the binding is an lvalue
|
||
|
// reference to the element type. Otherwise it's an rvalue reference to the
|
||
|
// element type.
|
||
|
{ auto [a] = wrap<int, void, int&>(); }
|
||
|
{ auto [a] = wrap<int&, void, int&>(); }
|
||
|
{ auto [a] = wrap<int&&, void, int&>(); } // ok, reference collapse to int&
|
||
|
|
||
|
{ auto [a] = wrap<int, void, int&&>(); }
|
||
|
{ auto [a] = wrap<int&, void, int&&>(); } // expected-error {{non-const lvalue reference to type 'int' cannot bind}} expected-note {{in implicit}}
|
||
|
{ auto [a] = wrap<const int&, void, int&&>(); }
|
||
|
{ auto [a] = wrap<int&&, void, int&&>(); }
|
||
|
|
||
|
{ auto [a] = wrap<int, void, float&>(); } // expected-error {{cannot bind}} expected-note {{implicit}}
|
||
|
{ auto [a] = wrap<const int, void, float&>(); } // ok, const int &a can bind to float
|
||
|
{ auto [a] = wrap<int, void, float>(); } // ok, int &&a can bind to float
|
||
|
}
|
||
|
|
||
|
namespace constant {
|
||
|
struct Q {};
|
||
|
template<int N> constexpr int get(Q &&) { return N * N; }
|
||
|
}
|
||
|
template<> struct std::tuple_size<constant::Q> { static const int value = 3; };
|
||
|
template<int N> struct std::tuple_element<N, constant::Q> { typedef int type; };
|
||
|
namespace constant {
|
||
|
Q q;
|
||
|
// This creates and lifetime-extends a temporary to hold the result of each get() call.
|
||
|
auto [a, b, c] = q; // expected-note {{temporary}}
|
||
|
static_assert(a == 0); // expected-error {{constant expression}} expected-note {{temporary}}
|
||
|
|
||
|
constexpr bool f() {
|
||
|
auto [a, b, c] = q;
|
||
|
return a == 0 && b == 1 && c == 4;
|
||
|
}
|
||
|
static_assert(f());
|
||
|
|
||
|
constexpr int g() {
|
||
|
int *p = nullptr;
|
||
|
{
|
||
|
auto [a, b, c] = q;
|
||
|
p = &c;
|
||
|
}
|
||
|
return *p; // expected-note {{read of object outside its lifetime}}
|
||
|
}
|
||
|
static_assert(g() == 4); // expected-error {{constant}} expected-note {{in call to 'g()'}}
|
||
|
}
|
||
|
|
||
|
// P0961R1
|
||
|
struct InvalidMemberGet {
|
||
|
int get();
|
||
|
template <class T> int get();
|
||
|
struct get {};
|
||
|
};
|
||
|
template <> struct std::tuple_size<InvalidMemberGet> { static constexpr size_t value = 1; };
|
||
|
template <> struct std::tuple_element<0, InvalidMemberGet> { typedef float type; };
|
||
|
template <size_t> float get(InvalidMemberGet) { return 0; }
|
||
|
int f() {
|
||
|
InvalidMemberGet img;
|
||
|
auto [x] = img;
|
||
|
typedef decltype(x) same_as_float;
|
||
|
typedef float same_as_float;
|
||
|
}
|
||
|
|
||
|
struct ValidMemberGet {
|
||
|
int get();
|
||
|
template <class T> int get() { return 0; }
|
||
|
template <size_t N> float get() { return 0; }
|
||
|
};
|
||
|
template <> struct std::tuple_size<ValidMemberGet> { static constexpr size_t value = 1; };
|
||
|
template <> struct std::tuple_element<0, ValidMemberGet> { typedef float type; };
|
||
|
// Don't use this one; we should use the member get.
|
||
|
template <size_t N> int get(ValidMemberGet) { static_assert(N && false, ""); }
|
||
|
int f2() {
|
||
|
ValidMemberGet img;
|
||
|
auto [x] = img;
|
||
|
typedef decltype(x) same_as_float;
|
||
|
typedef float same_as_float;
|
||
|
}
|
||
|
|
||
|
struct Base1 {
|
||
|
int get(); // expected-note{{member found by ambiguous name lookup}}
|
||
|
};
|
||
|
struct Base2 {
|
||
|
template<int> int get(); // expected-note{{member found by ambiguous name lookup}}
|
||
|
};
|
||
|
struct Derived : Base1, Base2 {};
|
||
|
|
||
|
template <> struct std::tuple_size<Derived> { static constexpr size_t value = 1; };
|
||
|
template <> struct std::tuple_element<0, Derived> { typedef int type; };
|
||
|
|
||
|
auto [x] = Derived(); // expected-error{{member 'get' found in multiple base classes of different types}}
|
||
|
|
||
|
struct Base {
|
||
|
template<int> int get();
|
||
|
};
|
||
|
struct UsingGet : Base {
|
||
|
using Base::get;
|
||
|
};
|
||
|
|
||
|
template <> struct std::tuple_size<UsingGet> {
|
||
|
static constexpr size_t value = 1;
|
||
|
};
|
||
|
template <> struct std::tuple_element<0, UsingGet> { typedef int type; };
|
||
|
|
||
|
auto [y] = UsingGet();
|