vector-conditional.cpp
11.4 KB
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// RUN: %clang_cc1 -triple x86_64-linux-pc -fsyntax-only -verify -fexceptions -fcxx-exceptions %s -std=c++17
// Note that this test depends on the size of long-long to be different from
// int, so it specifies a triple.
using FourShorts = short __attribute__((__vector_size__(8)));
using TwoInts = int __attribute__((__vector_size__(8)));
using TwoUInts = unsigned __attribute__((__vector_size__(8)));
using FourInts = int __attribute__((__vector_size__(16)));
using FourUInts = unsigned __attribute__((__vector_size__(16)));
using TwoLongLong = long long __attribute__((__vector_size__(16)));
using FourLongLong = long long __attribute__((__vector_size__(32)));
using TwoFloats = float __attribute__((__vector_size__(8)));
using FourFloats = float __attribute__((__vector_size__(16)));
using TwoDoubles = double __attribute__((__vector_size__(16)));
using FourDoubles = double __attribute__((__vector_size__(32)));
FourShorts four_shorts;
TwoInts two_ints;
TwoUInts two_uints;
FourInts four_ints;
FourUInts four_uints;
TwoLongLong two_ll;
FourLongLong four_ll;
TwoFloats two_floats;
FourFloats four_floats;
TwoDoubles two_doubles;
FourDoubles four_doubles;
enum E {};
enum class SE {};
E e;
SE se;
// Check the rules of the condition of the conditional operator.
void Condition() {
// Only int types are allowed here, the rest should fail to convert to bool.
(void)(four_floats ? 1 : 1); // expected-error {{is not contextually convertible to 'bool'}}}
(void)(two_doubles ? 1 : 1); // expected-error {{is not contextually convertible to 'bool'}}}
}
// Check the rules of the LHS/RHS of the conditional operator.
void Operands() {
(void)(four_ints ? four_ints : throw 1); // expected-error {{GNU vector conditional operand cannot be a throw expression}}
(void)(four_ints ? throw 1 : four_ints); // expected-error {{GNU vector conditional operand cannot be a throw expression}}
(void)(four_ints ?: throw 1); // expected-error {{GNU vector conditional operand cannot be a throw expression}}
(void)(four_ints ? (void)1 : four_ints); // expected-error {{GNU vector conditional operand cannot be void}}
(void)(four_ints ?: (void)1); // expected-error {{GNU vector conditional operand cannot be void}}
// Vector types must be the same element size as the condition.
(void)(four_ints ? two_ll : two_ll); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type 'TwoLongLong' (vector of 2 'long long' values) do not have the same number of elements}}
(void)(four_ints ? four_ll : four_ll); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type 'FourLongLong' (vector of 4 'long long' values) do not have elements of the same size}}
(void)(four_ints ? two_doubles : two_doubles); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type 'TwoDoubles' (vector of 2 'double' values) do not have the same number of elements}}
(void)(four_ints ? four_doubles : four_doubles); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type 'FourDoubles' (vector of 4 'double' values) do not have elements of the same size}}
(void)(four_ints ?: two_ints); // expected-error {{vector operands to the vector conditional must be the same type ('FourInts' (vector of 4 'int' values) and 'TwoInts' (vector of 2 'int' values)}}
(void)(four_ints ?: four_doubles); // expected-error {{vector operands to the vector conditional must be the same type ('FourInts' (vector of 4 'int' values) and 'FourDoubles' (vector of 4 'double' values)}}
// Scalars are promoted, but must be the same element size.
(void)(four_ints ? 3.0f : 3.0); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type '__attribute__((__vector_size__(4 * sizeof(double)))) double' (vector of 4 'double' values) do not have elements of the same size}}
(void)(four_ints ? 5ll : 5); // expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type '__attribute__((__vector_size__(4 * sizeof(long long)))) long long' (vector of 4 'long long' values) do not have elements of the same size}}
(void)(four_ints ?: 3.0); // expected-error {{cannot convert between scalar type 'double' and vector type 'FourInts' (vector of 4 'int' values) as implicit conversion would cause truncation}}
(void)(four_ints ?: 5ll); // We allow this despite GCc not allowing this since we support integral->vector-integral conversions despite integer rank.
// This one would be allowed in GCC, but we don't allow vectors of enum. Also,
// the error message isn't perfect, since it is only going to be a problem
// when both sides are an enum, otherwise it'll be promoted to whatever type
// the other side causes.
(void)(four_ints ? e : e); // expected-error {{enumeration type 'E' is not allowed in a vector conditional}}
(void)(four_ints ? se : se); // expected-error {{enumeration type 'SE' is not allowed in a vector conditional}}
(void)(four_shorts ? (short)5 : (unsigned short)5); // expected-error {{vector condition type 'FourShorts' (vector of 4 'short' values) and result type '__attribute__((__vector_size__(4 * sizeof(int)))) int' (vector of 4 'int' values) do not have elements of the same size}}
// They must also be convertible.
(void)(four_ints ? 3.0f : 5u);
(void)(four_ints ? 3.0f : 5);
unsigned us = 5u;
int sint = 5;
short shrt = 5;
unsigned short uss = 5u;
// The following 2 error in GCC for truncation errors, but it seems
// unimportant and inconsistent to enforce that rule.
(void)(four_ints ? 3.0f : us);
(void)(four_ints ? 3.0f : sint);
// Test promotion:
(void)(four_shorts ? uss : shrt); // expected-error {{vector condition type 'FourShorts' (vector of 4 'short' values) and result type '__attribute__((__vector_size__(4 * sizeof(int)))) int' (vector of 4 'int' values) do not have elements of the same size}}
(void)(four_shorts ? shrt : shrt); // should be fine.
(void)(four_ints ? uss : shrt); // should be fine, since they get promoted to int.
(void)(four_ints ? shrt : shrt); //expected-error {{vector condition type 'FourInts' (vector of 4 'int' values) and result type '__attribute__((__vector_size__(4 * sizeof(short)))) short' (vector of 4 'short' values) do not have elements of the same size}}
// Vectors must be the same type as eachother.
(void)(four_ints ? four_uints : four_floats); // expected-error {{vector operands to the vector conditional must be the same type ('FourUInts' (vector of 4 'unsigned int' values) and 'FourFloats' (vector of 4 'float' values))}}
(void)(four_ints ? four_uints : four_ints); // expected-error {{vector operands to the vector conditional must be the same type ('FourUInts' (vector of 4 'unsigned int' values) and 'FourInts' (vector of 4 'int' values))}}
(void)(four_ints ? four_ints : four_uints); // expected-error {{vector operands to the vector conditional must be the same type ('FourInts' (vector of 4 'int' values) and 'FourUInts' (vector of 4 'unsigned int' values))}}
// GCC rejects these, but our lax vector conversions don't seem to have a problem with them. Allow conversion of the float to an int as an extension.
(void)(four_ints ? four_uints : 3.0f);
(void)(four_ints ? four_ints : 3.0f);
// When there is a vector and a scalar, conversions must be legal.
(void)(four_ints ? four_floats : 3); // should work, ints can convert to floats.
(void)(four_ints ? four_uints : e); // should work, non-scoped enum can convert to uint.
(void)(four_ints ? four_uints : se); // expected-error {{cannot convert between vector and non-scalar values ('FourUInts' (vector of 4 'unsigned int' values) and 'SE'}}
// GCC permits this, but our conversion rules reject this for truncation.
(void)(two_ints ? two_ints : us); // expected-error {{cannot convert between scalar type 'unsigned int' and vector type 'TwoInts'}}
(void)(four_shorts ? four_shorts : uss); // expected-error {{cannot convert between scalar type 'unsigned short' and vector type 'FourShorts'}}
(void)(four_ints ? four_floats : us); // expected-error {{cannot convert between scalar type 'unsigned int' and vector type 'FourFloats'}}
(void)(four_ints ? four_floats : sint); // expected-error {{cannot convert between scalar type 'int' and vector type 'FourFloats'}}
}
template <typename T1, typename T2>
struct is_same {
static constexpr bool value = false;
};
template <typename T>
struct is_same<T, T> {
static constexpr bool value = true;
};
template <typename T1, typename T2>
constexpr bool is_same_v = is_same<T1, T2>::value;
template <typename T>
T &&declval();
// Check the result types when given two vector types.
void ResultTypes() {
// Vectors must be the same, but result is the type of the LHS/RHS.
static_assert(is_same_v<TwoInts, decltype(declval<TwoInts>() ? declval<TwoInts>() : declval<TwoInts>())>);
static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? declval<TwoFloats>() : declval<TwoFloats>())>);
// When both are scalars, converts to vectors of common type.
static_assert(is_same_v<TwoUInts, decltype(declval<TwoInts>() ? declval<int>() : declval<unsigned int>())>);
// Constant is allowed since it doesn't truncate, and should promote to float.
static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? declval<float>() : 5u)>);
static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? 5 : declval<float>())>);
// when only 1 is a scalar, it should convert to a compatible type.
static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? declval<TwoFloats>() : declval<float>())>);
static_assert(is_same_v<TwoInts, decltype(declval<TwoInts>() ? declval<TwoInts>() : declval<int>())>);
static_assert(is_same_v<TwoFloats, decltype(declval<TwoInts>() ? declval<TwoFloats>() : 5)>);
// For the Binary conditional operator, the result type is either the vector on the RHS (that fits the rules on size/count), or the scalar extended to the correct count.
static_assert(is_same_v<TwoInts, decltype(declval<TwoInts>() ?: declval<TwoInts>())>);
static_assert(is_same_v<TwoInts, decltype(declval<TwoInts>() ?: declval<int>())>);
}
template <typename Cond>
void dependent_cond(Cond C) {
(void)(C ? 1 : 2);
}
template <typename Operand>
void dependent_operand(Operand C) {
(void)(two_ints ? 1 : C);
(void)(two_ints ? C : 1);
(void)(two_ints ? C : C);
}
template <typename Cond, typename LHS, typename RHS>
void all_dependent(Cond C, LHS L, RHS R) {
(void)(C ? L : R);
}
// Check dependent cases.
void Templates() {
dependent_cond(two_ints);
dependent_operand(two_floats);
// expected-error@159 {{vector operands to the vector conditional must be the same type ('__attribute__((__vector_size__(4 * sizeof(unsigned int)))) unsigned int' (vector of 4 'unsigned int' values) and '__attribute__((__vector_size__(4 * sizeof(double)))) double' (vector of 4 'double' values))}}}
all_dependent(four_ints, four_uints, four_doubles); // expected-note {{in instantiation of}}
// expected-error@159 {{vector operands to the vector conditional must be the same type ('__attribute__((__vector_size__(4 * sizeof(unsigned int)))) unsigned int' (vector of 4 'unsigned int' values) and '__attribute__((__vector_size__(2 * sizeof(unsigned int)))) unsigned int' (vector of 2 'unsigned int' values))}}}
all_dependent(four_ints, four_uints, two_uints); // expected-note {{in instantiation of}}
all_dependent(four_ints, four_uints, four_uints);
}