Skip to content

Commit b449892

Browse files
Better error message for duplicate property name
1 parent 10b757f commit b449892

File tree

2 files changed

+73
-21
lines changed

2 files changed

+73
-21
lines changed

sycl/include/sycl/ext/oneapi/properties/properties.hpp

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ namespace sycl {
3131
inline namespace _V1 {
3232
namespace ext::oneapi::experimental {
3333
namespace new_properties {
34+
35+
template <typename properties_type_list_ty, typename = void>
36+
class __SYCL_EBO properties;
37+
3438
namespace detail {
3539
template <typename... property_tys> struct properties_type_list;
3640

@@ -112,18 +116,22 @@ struct property_base : property_key_tag<property_key_t> {
112116
return *static_cast<const property_t *>(this);
113117
}
114118
}
119+
120+
// For key_t access in error reporting specialization.
121+
template <typename, typename>
122+
friend class __SYCL_EBO new_properties::properties;
115123
};
116124

117125
template <typename... property_tys>
118-
inline constexpr bool property_keys_are_unique = []() constexpr {
126+
inline constexpr bool property_names_are_unique = []() constexpr {
119127
if constexpr (sizeof...(property_tys) == 0) {
120128
return true;
121129
} else {
122-
const std::array keys = {property_tys::property_name...};
123-
auto N = keys.size();
130+
const std::array names = {property_tys::property_name...};
131+
auto N = names.size();
124132
for (int i = 0; i < N; ++i)
125133
for (int j = i + 1; j < N; ++j)
126-
if (keys[i] == keys[j])
134+
if (names[i] == names[j])
127135
return false;
128136

129137
return true;
@@ -135,23 +143,21 @@ inline constexpr bool properties_are_sorted = []() constexpr {
135143
if constexpr (sizeof...(property_tys) == 0) {
136144
return true;
137145
} else {
138-
const std::array sort_keys = {property_tys::property_name...};
146+
const std::array sort_names = {property_tys::property_name...};
139147
// std::is_sorted isn't constexpr until C++20.
140148
//
141-
// Sorting is an implementation detail while uniqueness of the property_keys
142-
// is an API restriction. This internal check actually combines both
143-
// conditions as we expect that user error is handled before the internal
144-
// `properties_are_sorted` assert is checked.
145-
for (std::size_t idx = 1; idx < sort_keys.size(); ++idx)
146-
if (sort_keys[idx - 1] >= sort_keys[idx])
149+
// Sorting is an implementation detail while uniqueness of the
150+
// property_name's is an API restriction. This internal check actually
151+
// combines both conditions as we expect that user error is handled before
152+
// the internal `properties_are_sorted` assert is checked.
153+
for (std::size_t idx = 1; idx < sort_names.size(); ++idx)
154+
if (sort_names[idx - 1] >= sort_names[idx])
147155
return false;
148156
return true;
149157
}
150158
}();
151159
} // namespace detail
152160

153-
template <typename properties_type_list_ty, typename = void> class properties;
154-
155161
template <typename T> struct is_property_list : std::false_type {};
156162
template <typename PropListTy>
157163
struct is_property_list<properties<PropListTy>> : std::true_type {};
@@ -164,20 +170,40 @@ inline constexpr bool is_property_v =
164170
!is_property_list_v<T>;
165171

166172
// Empty property list.
167-
template <> class properties<detail::properties_type_list<>, void> {
173+
template <> class __SYCL_EBO properties<detail::properties_type_list<>, void> {
168174
template <typename> static constexpr bool has_property() { return false; }
169175
};
170176

171177
// Base implementation to provide nice user error in case of mis-use. Without it
172178
// an error "base class '<property>' specified more than once as a direct base
173179
// class" is reported prior to static_assert's error.
174180
template <typename... property_tys>
175-
class properties<
181+
class __SYCL_EBO properties<
176182
detail::properties_type_list<property_tys...>,
177-
std::enable_if_t<!detail::property_keys_are_unique<property_tys...>>> {
183+
std::enable_if_t<!detail::property_names_are_unique<property_tys...>>> {
184+
185+
// This is a separate specialization to report an error, we can afford doing
186+
// extra work to provide nice error message without sacrificing compile time
187+
// on non-exceptional path. Let's find *a* pair of properties that failed the
188+
// check. Note that there might be multiple duplicate names, we're only
189+
// reporting one instance. Once user addresses that, the next pair will be
190+
// reported.
191+
static constexpr auto conflict = []() constexpr {
192+
const std::array keys = {property_tys::property_name...};
193+
auto N = keys.size();
194+
for (int i = 0; i < N; ++i)
195+
for (int j = i + 1; j < N; ++j)
196+
if (keys[i] == keys[j])
197+
return std::pair{i, j};
198+
}();
199+
using first_type = detail::nth_type_t<conflict.first, property_tys...>;
200+
using second_type = detail::nth_type_t<conflict.second, property_tys...>;
201+
static_assert(
202+
!std::is_same_v<typename first_type::key_t, typename second_type::key_t>,
203+
"Duplicate property!");
204+
static_assert(first_type::property_name != second_type::property_name,
205+
"Property name collision between different property keys!");
178206
static_assert((is_property_v<property_tys> && ...));
179-
static_assert(detail::property_keys_are_unique<property_tys...>,
180-
"Property keys must be unique");
181207
};
182208

183209
template <typename... property_tys>

sycl/test/extensions/properties/new_properties_negative.cpp

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,39 @@ struct named_property_base
2525

2626
template <int N> struct property : named_property_base<property<N>> {};
2727

28+
template <int N>
29+
struct property_with_key
30+
: named_property_base<property_with_key<N>, struct prop_key_t> {};
31+
32+
namespace library_a {
33+
struct prop : detail::property_base<prop> {
34+
// Wrong, violates the extension specification! Property name must include
35+
// library namespace to avoid collisions with other libraries!
36+
static constexpr std::string_view property_name{"prop"};
37+
};
38+
}
39+
namespace library_b {
40+
struct prop : detail::property_base<prop> {
41+
// Wrong, violates the extension specification! Property name must include
42+
// library namespace to avoid collisions with other libraries!
43+
static constexpr std::string_view property_name{"prop"};
44+
};
45+
}
46+
2847
void test() {
29-
// expected-error-re@sycl/ext/oneapi/properties/properties.hpp:* {{static assertion failed due to requirement {{.+}}: Property keys must be unique}}
48+
// expected-error@sycl/ext/oneapi/properties/properties.hpp:* {{static assertion failed due to requirement '!std::is_same_v<property<1>, property<1>>': Duplicate property!}}
3049
std::ignore = properties{property<1>{}, property<1>{}};
3150

3251
constexpr properties pl{property<1>{}, property<2>{}};
33-
// expected-error-re@sycl/ext/oneapi/properties/properties.hpp:* {{static assertion failed due to requirement {{.+}}: Property keys must be unique}}
34-
std::ignore = properties{pl, property<1>{}};
52+
// expected-error@sycl/ext/oneapi/properties/properties.hpp:* {{static assertion failed due to requirement '!std::is_same_v<property<2>, property<2>>': Duplicate property!}}
53+
std::ignore = properties{pl, property<2>{}};
54+
55+
// Unfortunately, C++ front end doesn't use qualified name for "prop" below...
56+
// expected-error@sycl/ext/oneapi/properties/properties.hpp:* {{static assertion failed due to requirement 'prop::property_name != prop::property_name': Property name collision between different property keys!}}
57+
std::ignore = properties{library_a::prop{}, library_b::prop{}};
58+
59+
// expected-error@sycl/ext/oneapi/properties/properties.hpp:* {{static assertion failed due to requirement '!std::is_same_v<prop_key_t, prop_key_t>': Duplicate property!}}
60+
std::ignore = properties{property_with_key<1>{}, property_with_key<2>{}};
3561
}
3662

3763

0 commit comments

Comments
 (0)