[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
This revision was not accepted when it landed; it landed in state "Needs Review". This revision was automatically updated to reflect the committed changes. Closed by commit rGa0839b14df6d: [libc++] Fix tuple assignment from types derived from a tuple-like (authored by ldionne). Changed prior to commit: https://reviews.llvm.org/D50106?vs=325449=325524#toc Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 Files: libcxx/include/tuple libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/array.extension.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/support/propagate_value_category.hpp Index: libcxx/test/support/propagate_value_category.hpp === --- /dev/null +++ libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,153 @@ +//===--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY +#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY + +#include "test_macros.h" +#include + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 +#endif + +using UnderlyingVCType = unsigned; +enum ValueCategory : UnderlyingVCType { + VC_None = 0, + VC_LVal = 1 << 0, + VC_RVal = 1 << 1, + VC_Const = 1 << 2, + VC_Volatile = 1 << 3, + VC_ConstVolatile = VC_Const | VC_Volatile +}; + +inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS & (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS | (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS ^ (UnderlyingVCType)RHS); +} + +inline constexpr bool isValidValueCategory(ValueCategory VC) { + return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal); +} + +inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) { + return Arg == Key || ((Arg & Key) == Key); +} + +template +using UnCVRef = +typename std::remove_cv::type>::type; + +template +constexpr ValueCategory getReferenceQuals() { + return std::is_lvalue_reference::value + ? VC_LVal + : (std::is_rvalue_reference::value ? VC_RVal : VC_None); +} +static_assert(getReferenceQuals() == VC_None, ""); +static_assert(getReferenceQuals() == VC_LVal, ""); +static_assert(getReferenceQuals() == VC_RVal, ""); + +template +constexpr ValueCategory getCVQuals() { + using Vp = typename std::remove_reference::type; + return std::is_const::value && std::is_volatile::value + ? VC_ConstVolatile + : (std::is_const::value +? VC_Const +: (std::is_volatile::value ? VC_Volatile : VC_None)); +} +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); +static_assert(getCVQuals() == VC_Volatile, ""); +static_assert(getCVQuals() == VC_ConstVolatile, ""); +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); + +template +inline constexpr ValueCategory getValueCategory() { + return getReferenceQuals() | getCVQuals(); +} +static_assert(getValueCategory() == VC_None, ""); +static_assert(getValueCategory() == (VC_LVal | VC_Const), ""); +static_assert(getValueCategory() == + (VC_RVal | VC_ConstVolatile), + ""); + +template +struct ApplyValueCategory { +private: + static_assert(isValidValueCategory(VC), ""); + + template + using CondT = typename std::conditional::type; + +public: + template > + using ApplyCVQuals = CondT< + hasValueCategory(VC, VC_ConstVolatile), typename std::add_cv::type, + CondT::type, +CondT::type, Tp>>>; + + template ::type> + using
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
ldionne updated this revision to Diff 325449. ldionne added a comment. Rebase onto main. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 Files: libcxx/include/tuple libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/array.extension.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/support/propagate_value_category.hpp Index: libcxx/test/support/propagate_value_category.hpp === --- /dev/null +++ libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,153 @@ +//===--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY +#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY + +#include "test_macros.h" +#include + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 +#endif + +using UnderlyingVCType = unsigned; +enum ValueCategory : UnderlyingVCType { + VC_None = 0, + VC_LVal = 1 << 0, + VC_RVal = 1 << 1, + VC_Const = 1 << 2, + VC_Volatile = 1 << 3, + VC_ConstVolatile = VC_Const | VC_Volatile +}; + +inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS & (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS | (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS ^ (UnderlyingVCType)RHS); +} + +inline constexpr bool isValidValueCategory(ValueCategory VC) { + return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal); +} + +inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) { + return Arg == Key || ((Arg & Key) == Key); +} + +template +using UnCVRef = +typename std::remove_cv::type>::type; + +template +constexpr ValueCategory getReferenceQuals() { + return std::is_lvalue_reference::value + ? VC_LVal + : (std::is_rvalue_reference::value ? VC_RVal : VC_None); +} +static_assert(getReferenceQuals() == VC_None, ""); +static_assert(getReferenceQuals() == VC_LVal, ""); +static_assert(getReferenceQuals() == VC_RVal, ""); + +template +constexpr ValueCategory getCVQuals() { + using Vp = typename std::remove_reference::type; + return std::is_const::value && std::is_volatile::value + ? VC_ConstVolatile + : (std::is_const::value +? VC_Const +: (std::is_volatile::value ? VC_Volatile : VC_None)); +} +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); +static_assert(getCVQuals() == VC_Volatile, ""); +static_assert(getCVQuals() == VC_ConstVolatile, ""); +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); + +template +inline constexpr ValueCategory getValueCategory() { + return getReferenceQuals() | getCVQuals(); +} +static_assert(getValueCategory() == VC_None, ""); +static_assert(getValueCategory() == (VC_LVal | VC_Const), ""); +static_assert(getValueCategory() == + (VC_RVal | VC_ConstVolatile), + ""); + +template +struct ApplyValueCategory { +private: + static_assert(isValidValueCategory(VC), ""); + + template + using CondT = typename std::conditional::type; + +public: + template > + using ApplyCVQuals = CondT< + hasValueCategory(VC, VC_ConstVolatile), typename std::add_cv::type, + CondT::type, +CondT::type, Tp>>>; + + template ::type> + using ApplyReferenceQuals = + CondT::type, +CondT::type, Vp>>; + + template + using Apply = ApplyReferenceQuals>>; + + template ::type = true> + static Apply> cast(Tp &) { +using ToType = Apply>; +return static_cast(t); + } + + template ::type = true>
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
ldionne added a comment. I think we should be pretty much good to go now. Will let CI run just in case. Comment at: libcxx/include/tuple:971 +_VSTD::get<0>(*this) = _VSTD::forward<_Up1>(__pair.first); +_VSTD::get<1>(*this) = _VSTD::forward<_Up2>(__pair.second); +return *this; Quuxplusone wrote: > Oh, late-breaking nit: I would prefer to see `... = > static_cast<_Up1&&>(__pair.first);` here, because `_Up1` is not being deduced > according to forwarding-reference rules, and thus shouldn't be "forwarded." > Pragmatically I think `forward<_Up1>` ends up doing the right thing in all > cases... but I don't think it's appropriate here. (Plus, we save one function > template instantiation by omitting it!) It actually occurs to me that we should really be using `_VSTD::move(__pair.first)` since we're taking a rvalue reference to the pair as an argument. I'll change it to that, please LMK if you disagree. Comment at: libcxx/include/tuple:975 + +// EXTENSION +template Perhaps in a followup patch: could we delineate exactly what code is part of > this extension and put it all under an `#if _LIBCPP_TUPLE_ARRAY_ASSIGNMENT` > or something, such that we could say "this is the exact extent of the > extension code" and maybe even get customers to try compiling with > `-D_LIBCPP_TUPLE_ARRAY_ASSIGNMENT=0` to see what breaks? > > I'm assuming that our goal here is to deprecate and remove this extension > over time. But even if we support it forever, I think `#if` would be nicer > than the `//EXTENSION` comments. The goal is to remove the extension shortly after. Comment at: libcxx/include/tuple:992 +// EXTENSION +template Nitpick: Sent you a Slack suggesting how to eliminate this `class = void`. Replied there. The short story is that I used `class = void` to workaround the GCC segfaults I was seeing on the bots. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
ldionne updated this revision to Diff 325445. ldionne marked 6 inline comments as done. ldionne added a comment. Address Arthur's comments. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 Files: libcxx/include/tuple libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/array.extension.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/support/propagate_value_category.hpp Index: libcxx/test/support/propagate_value_category.hpp === --- /dev/null +++ libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,153 @@ +//===--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY +#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY + +#include "test_macros.h" +#include + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 +#endif + +using UnderlyingVCType = unsigned; +enum ValueCategory : UnderlyingVCType { + VC_None = 0, + VC_LVal = 1 << 0, + VC_RVal = 1 << 1, + VC_Const = 1 << 2, + VC_Volatile = 1 << 3, + VC_ConstVolatile = VC_Const | VC_Volatile +}; + +inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS & (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS | (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS ^ (UnderlyingVCType)RHS); +} + +inline constexpr bool isValidValueCategory(ValueCategory VC) { + return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal); +} + +inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) { + return Arg == Key || ((Arg & Key) == Key); +} + +template +using UnCVRef = +typename std::remove_cv::type>::type; + +template +constexpr ValueCategory getReferenceQuals() { + return std::is_lvalue_reference::value + ? VC_LVal + : (std::is_rvalue_reference::value ? VC_RVal : VC_None); +} +static_assert(getReferenceQuals() == VC_None, ""); +static_assert(getReferenceQuals() == VC_LVal, ""); +static_assert(getReferenceQuals() == VC_RVal, ""); + +template +constexpr ValueCategory getCVQuals() { + using Vp = typename std::remove_reference::type; + return std::is_const::value && std::is_volatile::value + ? VC_ConstVolatile + : (std::is_const::value +? VC_Const +: (std::is_volatile::value ? VC_Volatile : VC_None)); +} +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); +static_assert(getCVQuals() == VC_Volatile, ""); +static_assert(getCVQuals() == VC_ConstVolatile, ""); +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); + +template +inline constexpr ValueCategory getValueCategory() { + return getReferenceQuals() | getCVQuals(); +} +static_assert(getValueCategory() == VC_None, ""); +static_assert(getValueCategory() == (VC_LVal | VC_Const), ""); +static_assert(getValueCategory() == + (VC_RVal | VC_ConstVolatile), + ""); + +template +struct ApplyValueCategory { +private: + static_assert(isValidValueCategory(VC), ""); + + template + using CondT = typename std::conditional::type; + +public: + template > + using ApplyCVQuals = CondT< + hasValueCategory(VC, VC_ConstVolatile), typename std::add_cv::type, + CondT::type, +CondT::type, Tp>>>; + + template ::type> + using ApplyReferenceQuals = + CondT::type, +CondT::type, Vp>>; + + template + using Apply = ApplyReferenceQuals>>; + + template ::type = true> + static Apply> cast(Tp &) { +using ToType = Apply>; +return
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
Quuxplusone added inline comments. Comment at: libcxx/include/tuple:971 +_VSTD::get<0>(*this) = _VSTD::forward<_Up1>(__pair.first); +_VSTD::get<1>(*this) = _VSTD::forward<_Up2>(__pair.second); +return *this; Oh, late-breaking nit: I would prefer to see `... = static_cast<_Up1&&>(__pair.first);` here, because `_Up1` is not being deduced according to forwarding-reference rules, and thus shouldn't be "forwarded." Pragmatically I think `forward<_Up1>` ends up doing the right thing in all cases... but I don't think it's appropriate here. (Plus, we save one function template instantiation by omitting it!) Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
Quuxplusone accepted this revision. Quuxplusone added a comment. Suggested some ways to improve test coverage, and continued bikeshedding on the SFINAE ;) but there's no reason to hold this up AFAIC. Comment at: libcxx/include/tuple:58 tuple& operator=(const tuple&); -tuple& -operator=(tuple&&) noexcept(AND(is_nothrow_move_assignable::value ...)); +tuple& operator=(tuple&&) noexcept(AND(is_nothrow_move_assignable::value ...)); template Since this already isn't mimicking the syntax of http://eel.is/c++draft/tuple.tuple#tuple.assign-1 , I think you should say tuple& operator=(tuple&&) noexcept(is_nothrow_move_assignable_v && ...); but it definitely doesn't matter. Comment at: libcxx/include/tuple:975 + +// EXTENSION +template Array; +static_assert(!std::is_nothrow_assignable::value, ""); +} I would like to see this file combined with rvalue_array.pass.cpp, and also test the currently missing two value categories: static_assert(std::is_nothrow_assignable::value, ""); static_assert(std::is_nothrow_assignable::value, ""); and also the negative cases: static_assert(!std::is_assignable&>::value, ""); static_assert(!std::is_assignable&&>::value, ""); static_assert(!std::is_assignable&>::value, ""); static_assert(!std::is_assignable&&>::value, ""); static_assert(!std::is_assignable&>::value, ""); static_assert(!std::is_assignable&&>::value, ""); static_assert(!std::is_assignable&>::value, ""); static_assert(!std::is_assignable&&>::value, ""); Comment at: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp:52 +typedef std::pair Pair; +static_assert(!std::is_nothrow_assignable::value, ""); +} Don't just test for lack-of-nothrow-assignability; test for assignability-but-maythrow: static_assert(std::is_assignable::value, ""); static_assert(!std::is_nothrow_assignable::value, ""); And as above, I'd prefer to see tests for `Pair&` and `Pair&&` and `const Pair&&` as well. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
ldionne updated this revision to Diff 325039. ldionne added a comment. Fix GCC issues. I swear I had done the same fix locally previously, but I must have messed something up with Git or `arc diff`. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 Files: libcxx/include/tuple libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/support/propagate_value_category.hpp Index: libcxx/test/support/propagate_value_category.hpp === --- /dev/null +++ libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,153 @@ +//===--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY +#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY + +#include "test_macros.h" +#include + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 +#endif + +using UnderlyingVCType = unsigned; +enum ValueCategory : UnderlyingVCType { + VC_None = 0, + VC_LVal = 1 << 0, + VC_RVal = 1 << 1, + VC_Const = 1 << 2, + VC_Volatile = 1 << 3, + VC_ConstVolatile = VC_Const | VC_Volatile +}; + +inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS & (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS | (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS ^ (UnderlyingVCType)RHS); +} + +inline constexpr bool isValidValueCategory(ValueCategory VC) { + return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal); +} + +inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) { + return Arg == Key || ((Arg & Key) == Key); +} + +template +using UnCVRef = +typename std::remove_cv::type>::type; + +template +constexpr ValueCategory getReferenceQuals() { + return std::is_lvalue_reference::value + ? VC_LVal + : (std::is_rvalue_reference::value ? VC_RVal : VC_None); +} +static_assert(getReferenceQuals() == VC_None, ""); +static_assert(getReferenceQuals() == VC_LVal, ""); +static_assert(getReferenceQuals() == VC_RVal, ""); + +template +constexpr ValueCategory getCVQuals() { + using Vp = typename std::remove_reference::type; + return std::is_const::value && std::is_volatile::value + ? VC_ConstVolatile + : (std::is_const::value +? VC_Const +: (std::is_volatile::value ? VC_Volatile : VC_None)); +} +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); +static_assert(getCVQuals() == VC_Volatile, ""); +static_assert(getCVQuals() == VC_ConstVolatile, ""); +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); + +template +inline constexpr ValueCategory getValueCategory() { + return getReferenceQuals() | getCVQuals(); +} +static_assert(getValueCategory() == VC_None, ""); +static_assert(getValueCategory() == (VC_LVal | VC_Const), ""); +static_assert(getValueCategory() == + (VC_RVal | VC_ConstVolatile), + ""); + +template +struct ApplyValueCategory { +private: + static_assert(isValidValueCategory(VC), ""); + + template + using CondT = typename std::conditional::type; + +public: + template > + using ApplyCVQuals = CondT< + hasValueCategory(VC, VC_ConstVolatile), typename std::add_cv::type, + CondT::type, +CondT::type, Tp>>>; + + template ::type> + using ApplyReferenceQuals = + CondT::type, +CondT::type, Vp>>; + + template
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
ldionne updated this revision to Diff 324648. ldionne added a comment. Address Arthur's comments. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 Files: libcxx/include/tuple libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/support/propagate_value_category.hpp Index: libcxx/test/support/propagate_value_category.hpp === --- /dev/null +++ libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,153 @@ +//===--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY +#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY + +#include "test_macros.h" +#include + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 +#endif + +using UnderlyingVCType = unsigned; +enum ValueCategory : UnderlyingVCType { + VC_None = 0, + VC_LVal = 1 << 0, + VC_RVal = 1 << 1, + VC_Const = 1 << 2, + VC_Volatile = 1 << 3, + VC_ConstVolatile = VC_Const | VC_Volatile +}; + +inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS & (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS | (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS ^ (UnderlyingVCType)RHS); +} + +inline constexpr bool isValidValueCategory(ValueCategory VC) { + return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal); +} + +inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) { + return Arg == Key || ((Arg & Key) == Key); +} + +template +using UnCVRef = +typename std::remove_cv::type>::type; + +template +constexpr ValueCategory getReferenceQuals() { + return std::is_lvalue_reference::value + ? VC_LVal + : (std::is_rvalue_reference::value ? VC_RVal : VC_None); +} +static_assert(getReferenceQuals() == VC_None, ""); +static_assert(getReferenceQuals() == VC_LVal, ""); +static_assert(getReferenceQuals() == VC_RVal, ""); + +template +constexpr ValueCategory getCVQuals() { + using Vp = typename std::remove_reference::type; + return std::is_const::value && std::is_volatile::value + ? VC_ConstVolatile + : (std::is_const::value +? VC_Const +: (std::is_volatile::value ? VC_Volatile : VC_None)); +} +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); +static_assert(getCVQuals() == VC_Volatile, ""); +static_assert(getCVQuals() == VC_ConstVolatile, ""); +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); + +template +inline constexpr ValueCategory getValueCategory() { + return getReferenceQuals() | getCVQuals(); +} +static_assert(getValueCategory() == VC_None, ""); +static_assert(getValueCategory() == (VC_LVal | VC_Const), ""); +static_assert(getValueCategory() == + (VC_RVal | VC_ConstVolatile), + ""); + +template +struct ApplyValueCategory { +private: + static_assert(isValidValueCategory(VC), ""); + + template + using CondT = typename std::conditional::type; + +public: + template > + using ApplyCVQuals = CondT< + hasValueCategory(VC, VC_ConstVolatile), typename std::add_cv::type, + CondT::type, +CondT::type, Tp>>>; + + template ::type> + using ApplyReferenceQuals = + CondT::type, +CondT::type, Vp>>; + + template + using Apply = ApplyReferenceQuals>>; + + template ::type = true> + static Apply> cast(Tp &) {
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
ldionne updated this revision to Diff 324645. ldionne added a comment. Fix build on GCC (or at least I think it should) Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 Files: libcxx/include/tuple libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/support/propagate_value_category.hpp Index: libcxx/test/support/propagate_value_category.hpp === --- /dev/null +++ libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,153 @@ +//===--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY +#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY + +#include "test_macros.h" +#include + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 +#endif + +using UnderlyingVCType = unsigned; +enum ValueCategory : UnderlyingVCType { + VC_None = 0, + VC_LVal = 1 << 0, + VC_RVal = 1 << 1, + VC_Const = 1 << 2, + VC_Volatile = 1 << 3, + VC_ConstVolatile = VC_Const | VC_Volatile +}; + +inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS & (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS | (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS ^ (UnderlyingVCType)RHS); +} + +inline constexpr bool isValidValueCategory(ValueCategory VC) { + return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal); +} + +inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) { + return Arg == Key || ((Arg & Key) == Key); +} + +template +using UnCVRef = +typename std::remove_cv::type>::type; + +template +constexpr ValueCategory getReferenceQuals() { + return std::is_lvalue_reference::value + ? VC_LVal + : (std::is_rvalue_reference::value ? VC_RVal : VC_None); +} +static_assert(getReferenceQuals() == VC_None, ""); +static_assert(getReferenceQuals() == VC_LVal, ""); +static_assert(getReferenceQuals() == VC_RVal, ""); + +template +constexpr ValueCategory getCVQuals() { + using Vp = typename std::remove_reference::type; + return std::is_const::value && std::is_volatile::value + ? VC_ConstVolatile + : (std::is_const::value +? VC_Const +: (std::is_volatile::value ? VC_Volatile : VC_None)); +} +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); +static_assert(getCVQuals() == VC_Volatile, ""); +static_assert(getCVQuals() == VC_ConstVolatile, ""); +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); + +template +inline constexpr ValueCategory getValueCategory() { + return getReferenceQuals() | getCVQuals(); +} +static_assert(getValueCategory() == VC_None, ""); +static_assert(getValueCategory() == (VC_LVal | VC_Const), ""); +static_assert(getValueCategory() == + (VC_RVal | VC_ConstVolatile), + ""); + +template +struct ApplyValueCategory { +private: + static_assert(isValidValueCategory(VC), ""); + + template + using CondT = typename std::conditional::type; + +public: + template > + using ApplyCVQuals = CondT< + hasValueCategory(VC, VC_ConstVolatile), typename std::add_cv::type, + CondT::type, +CondT::type, Tp>>>; + + template ::type> + using ApplyReferenceQuals = + CondT::type, +CondT::type, Vp>>; + + template + using Apply = ApplyReferenceQuals>>; + + template ::type = true> +
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
Quuxplusone added inline comments. Comment at: libcxx/include/tuple:432 +void __memberwise_copy_assign(_Dest& __dest, _Source const& __source, __tuple_indices<_Np...>) { +__swallow(((_VSTD::get<_Np>(__dest) = _VSTD::get<_Np>(__source)), void(), int())...); +} Your friendly neighborhood ADL hater says: `_VSTD::__swallow` plz (and line 438) (and maybe `int()` is a sillier-than-necessary way to write `0`?) Comment at: libcxx/include/tuple:891 +{ +__memberwise_copy_assign(*this, __tuple, +typename __make_tuple_indices::type()); Your friendly neighborhood ADL hater says: `_VSTD::` plz (and line 900) (and line 931) (and line 986) (and line 1002) Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
ldionne updated this revision to Diff 323047. ldionne added a comment. Fix segfault on GCC and a few incorrect checks in the type traits. I reduced the GCC segfault and it appears to have been fixed on GCC trunk, so I won't be filing a bug report. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 Files: libcxx/include/tuple libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/support/propagate_value_category.hpp Index: libcxx/test/support/propagate_value_category.hpp === --- /dev/null +++ libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,153 @@ +//===--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY +#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY + +#include "test_macros.h" +#include + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 +#endif + +using UnderlyingVCType = unsigned; +enum ValueCategory : UnderlyingVCType { + VC_None = 0, + VC_LVal = 1 << 0, + VC_RVal = 1 << 1, + VC_Const = 1 << 2, + VC_Volatile = 1 << 3, + VC_ConstVolatile = VC_Const | VC_Volatile +}; + +inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS & (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS | (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS ^ (UnderlyingVCType)RHS); +} + +inline constexpr bool isValidValueCategory(ValueCategory VC) { + return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal); +} + +inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) { + return Arg == Key || ((Arg & Key) == Key); +} + +template +using UnCVRef = +typename std::remove_cv::type>::type; + +template +constexpr ValueCategory getReferenceQuals() { + return std::is_lvalue_reference::value + ? VC_LVal + : (std::is_rvalue_reference::value ? VC_RVal : VC_None); +} +static_assert(getReferenceQuals() == VC_None, ""); +static_assert(getReferenceQuals() == VC_LVal, ""); +static_assert(getReferenceQuals() == VC_RVal, ""); + +template +constexpr ValueCategory getCVQuals() { + using Vp = typename std::remove_reference::type; + return std::is_const::value && std::is_volatile::value + ? VC_ConstVolatile + : (std::is_const::value +? VC_Const +: (std::is_volatile::value ? VC_Volatile : VC_None)); +} +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); +static_assert(getCVQuals() == VC_Volatile, ""); +static_assert(getCVQuals() == VC_ConstVolatile, ""); +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); + +template +inline constexpr ValueCategory getValueCategory() { + return getReferenceQuals() | getCVQuals(); +} +static_assert(getValueCategory() == VC_None, ""); +static_assert(getValueCategory() == (VC_LVal | VC_Const), ""); +static_assert(getValueCategory() == + (VC_RVal | VC_ConstVolatile), + ""); + +template +struct ApplyValueCategory { +private: + static_assert(isValidValueCategory(VC), ""); + + template + using CondT = typename std::conditional::type; + +public: + template > + using ApplyCVQuals = CondT< + hasValueCategory(VC, VC_ConstVolatile), typename std::add_cv::type, + CondT::type, +CondT::type, Tp>>>; + + template ::type> + using ApplyReferenceQuals = +
[PATCH] D50106: [libc++] Fix tuple assignment from types derived from a tuple-like
ldionne updated this revision to Diff 322746. ldionne retitled this revision from "[libc++] Fix tuple assignment from type derived from a tuple-like" to "[libc++] Fix tuple assignment from types derived from a tuple-like". ldionne edited the summary of this revision. ldionne added a comment. Herald added a project: libc++. Herald added a reviewer: libc++. Fix issues with the laziness of how we evaluate type traits Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D50106/new/ https://reviews.llvm.org/D50106 Files: libcxx/include/tuple libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp libcxx/test/support/propagate_value_category.hpp Index: libcxx/test/support/propagate_value_category.hpp === --- /dev/null +++ libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,153 @@ +//===--===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===--===// + +#ifndef TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY +#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY + +#include "test_macros.h" +#include + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 +#endif + +using UnderlyingVCType = unsigned; +enum ValueCategory : UnderlyingVCType { + VC_None = 0, + VC_LVal = 1 << 0, + VC_RVal = 1 << 1, + VC_Const = 1 << 2, + VC_Volatile = 1 << 3, + VC_ConstVolatile = VC_Const | VC_Volatile +}; + +inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS & (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS | (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS ^ (UnderlyingVCType)RHS); +} + +inline constexpr bool isValidValueCategory(ValueCategory VC) { + return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal); +} + +inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) { + return Arg == Key || ((Arg & Key) == Key); +} + +template +using UnCVRef = +typename std::remove_cv::type>::type; + +template +constexpr ValueCategory getReferenceQuals() { + return std::is_lvalue_reference::value + ? VC_LVal + : (std::is_rvalue_reference::value ? VC_RVal : VC_None); +} +static_assert(getReferenceQuals() == VC_None, ""); +static_assert(getReferenceQuals() == VC_LVal, ""); +static_assert(getReferenceQuals() == VC_RVal, ""); + +template +constexpr ValueCategory getCVQuals() { + using Vp = typename std::remove_reference::type; + return std::is_const::value && std::is_volatile::value + ? VC_ConstVolatile + : (std::is_const::value +? VC_Const +: (std::is_volatile::value ? VC_Volatile : VC_None)); +} +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); +static_assert(getCVQuals() == VC_Volatile, ""); +static_assert(getCVQuals() == VC_ConstVolatile, ""); +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); + +template +inline constexpr ValueCategory getValueCategory() { + return getReferenceQuals() | getCVQuals(); +} +static_assert(getValueCategory() == VC_None, ""); +static_assert(getValueCategory() == (VC_LVal | VC_Const), ""); +static_assert(getValueCategory() == + (VC_RVal | VC_ConstVolatile), + ""); + +template +struct ApplyValueCategory { +private: + static_assert(isValidValueCategory(VC), ""); + + template + using CondT = typename std::conditional::type; + +public: + template > + using ApplyCVQuals = CondT< +