From 3c14a7ddf168bf240b185ccc02f0c2132d137c7a Mon Sep 17 00:00:00 2001 From: Nathaniel Wesley Filardo Date: Mon, 23 Aug 2021 21:24:16 +0100 Subject: [PATCH] NFC: Concepts and fixed-pointing interact poorly Fortunately, C++ taketh away and C++ giveth, both, so here we are: a way to detect if we're in the middle of definining a type that uses itself as a template parameter in a way that flows into a concept check and, if so, short-circuit out of the need to actually do any checks. Wonders never cease. --- src/ds/concept.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/ds/concept.h b/src/ds/concept.h index 4e489ed..b49545c 100644 --- a/src/ds/concept.h +++ b/src/ds/concept.h @@ -38,5 +38,34 @@ namespace snmalloc template concept ConceptSame = std::is_same::value; # endif + + /** + * Some of the types in snmalloc are circular in their definition and use + * templating as a lazy language to carefully tie knots and only pull on the + * whole mess once it's assembled. Unfortunately, concepts amount to eagerly + * demanding the result of the computation. If concepts come into play during + * the circular definition, they may see an incomplete type and so fail (with + * "incomplete type ... used in type trait expression" or similar). However, + * it turns out that SFINAE gives us a way to detect whether a template + * parameter refers to an incomplete type, and short circuit evaluation means + * we can bail on concept checking if we find ourselves in this situation. + * + * See https://devblogs.microsoft.com/oldnewthing/20190710-00/?p=102678 + * + * Unfortunately, C++20 concepts are not first-order things and, in + * particular, cannot themselves be template parameters. So while we would + * love to write a generic Lazy combinator, + * + * template concept C, typename T> + * concept Lazy = !is_type_complete_v || C(); + * + * this will instead have to be inlined at every definition (and referred to + * explicitly at call sites) until C++23 or later. + */ + template + constexpr bool is_type_complete_v = false; + template + constexpr bool is_type_complete_v> = true; + } // namespace snmalloc #endif