Skip to content

Commit 4154c18

Browse files
authored
[Clang] Fix another concept cache bug (#162272)
We previously failed to profile dependent CallExprs; this patch corrects that behavior. We now also profile canonical types whenever possible, since type sugar has little impact on hash computation.
1 parent 17efa57 commit 4154c18

File tree

2 files changed

+356
-4
lines changed

2 files changed

+356
-4
lines changed

clang/lib/Sema/SemaConcept.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,12 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
305305
if (!NTTP)
306306
return TraverseDecl(D);
307307

308+
if (NTTP->getDepth() >= TemplateArgs.getNumLevels())
309+
return true;
310+
311+
if (!TemplateArgs.hasTemplateArgument(NTTP->getDepth(), NTTP->getIndex()))
312+
return true;
313+
308314
TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());
309315
if (NTTP->isParameterPack() && SemaRef.ArgPackSubstIndex) {
310316
assert(Arg.getKind() == TemplateArgument::Pack &&
@@ -331,17 +337,25 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
331337
return inherited::TraverseDecl(D);
332338
}
333339

340+
bool TraverseCallExpr(CallExpr *CE) {
341+
inherited::TraverseStmt(CE->getCallee());
342+
343+
for (Expr *Arg : CE->arguments())
344+
inherited::TraverseStmt(Arg);
345+
346+
return true;
347+
}
348+
334349
bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier = true) {
335350
// We don't care about TypeLocs. So traverse Types instead.
336-
return TraverseType(TL.getType(), TraverseQualifier);
351+
return TraverseType(TL.getType().getCanonicalType(), TraverseQualifier);
337352
}
338353

339354
bool TraverseTagType(const TagType *T, bool TraverseQualifier) {
340355
// T's parent can be dependent while T doesn't have any template arguments.
341356
// We should have already traversed its qualifier.
342357
// FIXME: Add an assert to catch cases where we failed to profile the
343-
// concept. assert(!T->isDependentType() && "We missed a case in profiling
344-
// concepts!");
358+
// concept.
345359
return true;
346360
}
347361

@@ -706,7 +720,6 @@ ExprResult ConstraintSatisfactionChecker::Evaluate(
706720

707721
if (auto Iter = S.UnsubstitutedConstraintSatisfactionCache.find(ID);
708722
Iter != S.UnsubstitutedConstraintSatisfactionCache.end()) {
709-
710723
auto &Cached = Iter->second.Satisfaction;
711724
Satisfaction.ContainsErrors = Cached.ContainsErrors;
712725
Satisfaction.IsSatisfied = Cached.IsSatisfied;

clang/test/SemaCXX/GH161671.cpp

Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
// RUN: %clang_cc1 -std=c++20 -w %s
2+
// RUN: %clang_cc1 -std=c++2c -w %s
3+
// expected-no-diagnostics
4+
5+
namespace std {
6+
template <typename _Tp, _Tp __v> struct integral_constant {
7+
static constexpr _Tp value = __v;
8+
using value_type = _Tp;
9+
};
10+
template <bool __v> using __bool_constant = integral_constant<bool, __v>;
11+
template <typename> struct is_integral : integral_constant<bool, true> {};
12+
template <typename> struct is_signed : integral_constant<bool, false> {};
13+
template <typename _Tp, typename _Up = _Tp> _Up __declval(int);
14+
template <typename _Tp> auto declval() -> decltype(__declval<_Tp>(0));
15+
template <typename> struct make_unsigned {
16+
using type = int;
17+
};
18+
template <typename _Tp> struct decay {
19+
using type = _Tp;
20+
};
21+
template <int, typename _Iftrue, typename> struct conditional {
22+
using type = _Iftrue;
23+
};
24+
} // namespace std
25+
namespace meta {
26+
template <template <typename...> class> struct quote;
27+
template <template <typename> class C, typename... Ts>
28+
concept valid = requires { typename C<Ts...>; };
29+
template <typename T>
30+
concept trait = requires { typename T; };
31+
template <typename T>
32+
concept invocable = requires { typename quote<T::template invoke>; };
33+
template <typename T>
34+
concept integral = requires { T::value; };
35+
template <trait T> using _t = T::type;
36+
template <integral T> constexpr T::value_type _v = T::value;
37+
template <bool B> using bool_ = std::integral_constant<bool, B>;
38+
template <invocable Fn, typename... Args>
39+
using invoke = Fn::template invoke<Args...>;
40+
template <typename> struct id;
41+
namespace detail {
42+
template <template <typename> class, typename...> struct defer_;
43+
template <template <typename> class C, typename... Ts>
44+
requires valid<C, Ts...>
45+
struct defer_<C, Ts...> {
46+
using type = C<Ts...>;
47+
};
48+
} // namespace detail
49+
template <template <typename> class C, typename... Ts>
50+
struct defer : detail::defer_<C, Ts...> {};
51+
template <template <typename...> class C> struct quote {
52+
template <typename... Ts> using invoke = _t<defer<C, Ts...>>;
53+
};
54+
namespace detail {
55+
template <int> struct _cond {
56+
template <typename Then, typename> using invoke = Then;
57+
};
58+
template <> struct _cond<false>;
59+
} // namespace detail
60+
template <bool If, typename Then, typename Else>
61+
using conditional_t = detail::_cond<If>::template invoke<Then, Else>;
62+
namespace detail {
63+
template <typename...> struct _if_;
64+
template <typename If, typename Then, typename Else>
65+
struct _if_<If, Then, Else> : std::conditional<_v<If>, Then, Else> {};
66+
} // namespace detail
67+
template <bool If, typename... Args>
68+
using if_c = _t<detail::_if_<bool_<If>, Args...>>;
69+
} // namespace meta
70+
template <bool> void requires_();
71+
template <typename A, typename B>
72+
concept same_as = __is_same(B, A);
73+
namespace ranges {
74+
template <typename> struct view_closure;
75+
template <typename T> using decay_t = meta::_t<std::decay<T>>;
76+
enum cardinality { unknown };
77+
template <cardinality> struct basic_view {};
78+
} // namespace ranges
79+
namespace std {
80+
template <typename> struct vector {};
81+
} // namespace std
82+
namespace ranges {
83+
struct {
84+
template <typename F, typename... Args>
85+
auto operator()(F f, Args... args) -> decltype(f(args...));
86+
} invoke;
87+
template <typename Fun, typename... Args>
88+
using invoke_result_t =
89+
decltype(invoke(std::declval<Fun>(), std::declval<Args>()...));
90+
namespace detail {
91+
struct with_difference_type_;
92+
template <typename T> using iter_value_t_ = T ::value_type;
93+
} // namespace detail
94+
template <typename R> using iter_value_t = detail::iter_value_t_<R>;
95+
namespace detail {
96+
template <typename I>
97+
using iter_size_t =
98+
meta::_t<meta::conditional_t<std::is_integral<I>::value,
99+
std::make_unsigned<I>, meta::id<I>>>;
100+
template <typename D>
101+
concept signed_integer_like_impl_concept_ =
102+
std::integral_constant<bool, -D()>::value;
103+
template <typename D>
104+
concept signed_integer_like_ = signed_integer_like_impl_concept_<D>;
105+
} // namespace detail
106+
template <typename S, typename I>
107+
concept sized_sentinel_for_requires_ =
108+
requires(S s, I i) { requires_<same_as<I, decltype(i - s)>>; };
109+
template <typename S, typename I>
110+
concept sized_sentinel_for = sized_sentinel_for_requires_<S, I>;
111+
struct range_access {
112+
template <typename Rng>
113+
static auto begin_cursor(Rng rng) -> decltype(rng.begin_cursor());
114+
template <typename Cur, typename O>
115+
static auto distance_to(Cur pos, O other) -> decltype(pos.distance_to(other));
116+
};
117+
namespace detail {
118+
template <typename S, typename C>
119+
concept sized_sentinel_for_cursor_requires_ = requires(S s, C c) {
120+
requires_<signed_integer_like_<decltype(range_access::distance_to(c, s))>>;
121+
};
122+
template <typename S, typename C>
123+
concept sized_sentinel_for_cursor = sized_sentinel_for_cursor_requires_<S, C>;
124+
struct iterator_associated_types_base_ {
125+
typedef range_access value_type;
126+
};
127+
template <typename>
128+
using iterator_associated_types_base = iterator_associated_types_base_;
129+
} // namespace detail
130+
template <typename>
131+
struct basic_iterator : detail::iterator_associated_types_base<int> {};
132+
template <typename Cur2, typename Cur>
133+
requires detail::sized_sentinel_for_cursor<Cur2, Cur>
134+
void operator-(basic_iterator<Cur2>, basic_iterator<Cur>);
135+
namespace _begin_ {
136+
template <typename T>
137+
concept has_member_begin_requires_ = requires(T t) { t; };
138+
template <typename T>
139+
concept has_member_begin = has_member_begin_requires_<T>;
140+
struct _member_result_ {
141+
template <typename R>
142+
using invoke = decltype(static_cast<R (*)()>(nullptr)().begin());
143+
};
144+
struct _non_member_result_;
145+
struct fn {
146+
template <typename R>
147+
using _result_t =
148+
meta::invoke<meta::conditional_t<has_member_begin<R>, _member_result_,
149+
_non_member_result_>,
150+
R>;
151+
template <typename R> _result_t<R> operator()(R);
152+
};
153+
} // namespace _begin_
154+
_begin_::fn begin;
155+
namespace _end_ {
156+
template <typename>
157+
concept has_member_end_requires_ = requires { begin; };
158+
template <typename T>
159+
concept has_member_end = has_member_end_requires_<T>;
160+
struct _member_result_ {
161+
template <typename R>
162+
using invoke = decltype(static_cast<R (*)()>(nullptr)().end());
163+
};
164+
struct _non_member_result_;
165+
struct fn {
166+
template <typename R>
167+
using _result_t =
168+
meta::invoke<meta::conditional_t<has_member_end<R>, _member_result_,
169+
_non_member_result_>,
170+
R>;
171+
template <typename R> _result_t<R> operator()(R);
172+
};
173+
} // namespace _end_
174+
_end_::fn end;
175+
template <typename Rng>
176+
using iterator_t = decltype(begin(static_cast<Rng (*)()>(nullptr)()));
177+
template <typename Rng>
178+
using sentinel_t = decltype(end(static_cast<Rng (*)()>(nullptr)()));
179+
template <typename T>
180+
concept has_member_size_requires_ = requires(T t) { t.size(); };
181+
template <typename T>
182+
concept has_member_size = has_member_size_requires_<T>;
183+
struct _other_result_;
184+
struct _member_result_ {
185+
template <typename> using invoke = decltype(0);
186+
template <typename R>
187+
using _result_t = meta::invoke<
188+
meta::conditional_t<has_member_size<R>, _member_result_, _other_result_>,
189+
R>;
190+
template <typename R> _result_t<R> operator()(R r) { r.size(); }
191+
} size;
192+
template <typename Rng> using range_value_t = iter_value_t<iterator_t<Rng>>;
193+
namespace detail {
194+
template <cardinality Card>
195+
std::integral_constant<cardinality, Card> test_cardinality(basic_view<Card> *);
196+
}
197+
template <typename Rng>
198+
struct range_cardinality
199+
: meta::conditional_t<__is_same(Rng, Rng),
200+
decltype(detail::test_cardinality(
201+
static_cast<Rng *>(nullptr))),
202+
Rng> {};
203+
template <typename T>
204+
concept sized_range_requires_ = requires(T t) { size(t); };
205+
template <typename T>
206+
concept sized_range = sized_range_requires_<T>;
207+
namespace detail {
208+
template <int> struct dependent_ {
209+
template <typename T> using invoke = T;
210+
};
211+
} // namespace detail
212+
template <typename Derived, cardinality Cardinality>
213+
struct view_interface : basic_view<Cardinality> {
214+
template <bool B> using D = meta::invoke<detail::dependent_<B>, Derived>;
215+
Derived derived();
216+
template <bool True = true>
217+
requires sized_sentinel_for<sentinel_t<D<True>>, iterator_t<D<True>>>
218+
detail::iter_size_t<iterator_t<D<True>>> size() {
219+
derived().end() - derived().begin();
220+
}
221+
};
222+
struct {
223+
template <typename Fun> view_closure<Fun> operator()(Fun);
224+
} make_view_closure;
225+
struct view_closure_base {
226+
template <typename Rng, typename ViewFn>
227+
friend auto operator|(Rng rng, ViewFn vw) {
228+
return vw(rng);
229+
}
230+
};
231+
template <typename ViewFn> struct view_closure : view_closure_base, ViewFn {};
232+
namespace detail {
233+
template <typename Derived>
234+
using begin_cursor_t =
235+
decay_t<decltype(range_access::begin_cursor(std::declval<Derived>()))>;
236+
template <typename Derived>
237+
using facade_iterator_t = basic_iterator<begin_cursor_t<Derived>>;
238+
template <typename Derived>
239+
using facade_sentinel_t =
240+
meta::if_c<same_as<Derived, Derived>, facade_iterator_t<Derived>, Derived>;
241+
} // namespace detail
242+
template <typename Derived, cardinality Cardinality>
243+
struct view_facade : view_interface<Derived, Cardinality> {
244+
template <typename D = Derived> auto begin() -> detail::facade_iterator_t<D>;
245+
template <typename D = Derived> auto end() -> detail::facade_sentinel_t<D>;
246+
};
247+
template <typename Derived, cardinality Cardinality>
248+
struct view_adaptor : view_facade<Derived, Cardinality> {
249+
auto begin_cursor() -> decltype(0);
250+
};
251+
namespace detail {
252+
template <typename...> struct bind_back_fn_;
253+
template <typename Fn, typename Arg> struct bind_back_fn_<Fn, Arg> {
254+
template <typename... CallArgs>
255+
invoke_result_t<Fn, CallArgs..., Arg> operator()(CallArgs...);
256+
};
257+
template <typename Fn, typename... Args>
258+
using bind_back_fn = bind_back_fn_<Fn, Args...>;
259+
} // namespace detail
260+
struct {
261+
template <typename Fn, typename Arg1>
262+
detail::bind_back_fn<Fn, Arg1> operator()(Fn, Arg1);
263+
} bind_back;
264+
namespace detail {
265+
struct to_container {
266+
template <typename> struct fn;
267+
template <typename, typename> struct closure;
268+
};
269+
template <typename, typename, typename R>
270+
concept to_container_reserve = sized_range<R>;
271+
template <typename MetaFn, typename Rng>
272+
using container_t = meta::invoke<MetaFn, Rng>;
273+
struct to_container_closure_base {
274+
template <typename Rng, typename MetaFn, typename Fn>
275+
friend auto operator|(Rng rng, to_container::closure<MetaFn, Fn> fn) {
276+
return fn(rng);
277+
}
278+
};
279+
template <typename, typename Fn>
280+
struct to_container::closure : to_container_closure_base, Fn {};
281+
template <typename MetaFn> struct to_container::fn {
282+
template <typename Rng> void impl(Rng, std::__bool_constant<false>);
283+
template <typename Rng> void impl(Rng rng, std::__bool_constant<true>) {
284+
size(rng);
285+
}
286+
template <typename Rng> container_t<MetaFn, Rng> operator()(Rng rng) {
287+
using cont_t = container_t<MetaFn, Rng>;
288+
using iter_t = Rng;
289+
using use_reserve_t =
290+
meta::bool_<to_container_reserve<cont_t, iter_t, Rng>>;
291+
impl(rng, use_reserve_t{});
292+
}
293+
};
294+
template <typename MetaFn, typename Fn>
295+
using to_container_closure = to_container::closure<MetaFn, Fn>;
296+
template <typename MetaFn>
297+
using to_container_fn = to_container_closure<MetaFn, to_container::fn<MetaFn>>;
298+
template <template <typename> class ContT> struct from_range {
299+
template <typename Rng>
300+
static auto from_rng_(long)
301+
-> meta::invoke<meta::quote<ContT>, range_value_t<Rng>>;
302+
template <typename Rng> using invoke = decltype(from_rng_<Rng>(0));
303+
};
304+
} // namespace detail
305+
detail::to_container_fn<detail::from_range<std::vector>> to_vector;
306+
template <typename Rng>
307+
struct remove_if_view
308+
: view_adaptor<remove_if_view<Rng>, range_cardinality<Rng>::value> {};
309+
struct filter_base_fn {
310+
template <typename Rng, typename Pred>
311+
remove_if_view<Rng> operator()(Rng, Pred);
312+
template <typename Pred> auto operator()(Pred pred) {
313+
return make_view_closure(bind_back(filter_base_fn{}, pred));
314+
}
315+
} filter;
316+
namespace detail {
317+
struct promote_as_signed_;
318+
template <typename I>
319+
using iota_difference_t =
320+
meta::conditional_t<std::is_integral<I>::value, promote_as_signed_,
321+
with_difference_type_>;
322+
} // namespace detail
323+
template <typename, typename>
324+
struct iota_view : view_facade<iota_view<int, int>, unknown> {
325+
struct cursor {
326+
auto distance_to(cursor) -> detail::iota_difference_t<int>;
327+
};
328+
cursor begin_cursor();
329+
};
330+
struct {
331+
template <typename From, typename To>
332+
requires(std::is_signed<From>::value == std::is_signed<To>::value)
333+
iota_view<From, To> operator()(From, To);
334+
} iota;
335+
} // namespace ranges
336+
void foo() {
337+
ranges::iota(0, 1) | ranges::to_vector =
338+
ranges::iota(0, 1) | ranges::filter([] {}) | ranges::to_vector;
339+
}

0 commit comments

Comments
 (0)