diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 69d89d6493ca86..5b668d57ed7ae6 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -997,7 +997,7 @@ public: let HasCustomParsing = 1; let TemplateDependent = 1; - let Documentation = [Undocumented]; + let Documentation = [InternalOnly]; } def ARMInterrupt : InheritableAttr, TargetSpecificAttr { diff --git a/clang/lib/AST/APValue.cpp b/clang/lib/AST/APValue.cpp index 3c65393d7e09a4..e0b668e227db43 100644 --- a/clang/lib/AST/APValue.cpp +++ b/clang/lib/AST/APValue.cpp @@ -513,9 +513,7 @@ static void profileReflection(llvm::FoldingSetNodeID &ID, APValue V) { if (auto *TST = dyn_cast(QT)) { // Note: This sugar only kept for alias template specializations. ID.AddInteger(Type::TemplateSpecialization); - ID.AddPointer(TST->getTemplateName().getAsTemplateDecl()); - if (auto *D = QT->getAsRecordDecl()) - ID.AddPointer(D->getCanonicalDecl()); + QT.getCanonicalType().Profile(ID); } else { ID.AddInteger(0); if (auto *TDT = dyn_cast(QT)) { @@ -530,8 +528,8 @@ static void profileReflection(llvm::FoldingSetNodeID &ID, APValue V) { } case ReflectionKind::Declaration: if (auto *PVD = dyn_cast(V.getReflectedDecl())) { - auto *FD = cast(PVD->getDeclContext())->getFirstDecl(); - PVD = FD->getParamDecl(PVD->getFunctionScopeIndex()); + if (auto *FD = dyn_cast(PVD->getDeclContext())) + PVD = FD->getFirstDecl()->getParamDecl(PVD->getFunctionScopeIndex()); ID.AddPointer(PVD); } else { ID.AddPointer(V.getReflectedDecl()); diff --git a/clang/lib/AST/ExprConstantMeta.cpp b/clang/lib/AST/ExprConstantMeta.cpp index 34b59f3bf92395..732fc4a6c55364 100644 --- a/clang/lib/AST/ExprConstantMeta.cpp +++ b/clang/lib/AST/ExprConstantMeta.cpp @@ -209,6 +209,11 @@ static bool is_volatile(APValue &Result, ASTContext &C, MetaActions &Meta, EvalFn Evaluator, DiagFn Diagnoser, QualType ResultTy, SourceRange Range, ArrayRef Args); +static bool is_mutable_member(APValue &Result, ASTContext &C, MetaActions &Meta, + EvalFn Evaluator, DiagFn Diagnoser, + QualType ResultTy, SourceRange Range, + ArrayRef Args); + static bool is_lvalue_reference_qualified(APValue &Result, ASTContext &C, MetaActions &Meta, EvalFn Evaluator, DiagFn Diagnoser, QualType ResultTy, @@ -611,6 +616,7 @@ static constexpr Metafunction Metafunctions[] = { { Metafunction::MFRK_bool, 1, 1, is_enumerator }, { Metafunction::MFRK_bool, 1, 1, is_const }, { Metafunction::MFRK_bool, 1, 1, is_volatile }, + { Metafunction::MFRK_bool, 1, 1, is_mutable_member }, { Metafunction::MFRK_bool, 1, 1, is_lvalue_reference_qualified }, { Metafunction::MFRK_bool, 1, 1, is_rvalue_reference_qualified }, { Metafunction::MFRK_bool, 1, 1, has_static_storage_duration }, @@ -3494,6 +3500,38 @@ bool is_volatile(APValue &Result, ASTContext &C, MetaActions &Meta, llvm_unreachable("invalid reflection type"); } +bool is_mutable_member(APValue &Result, ASTContext &C, MetaActions &Meta, + EvalFn Evaluator, DiagFn Diagnoser, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == C.BoolTy); + + APValue RV; + if (!Evaluator(RV, Args[0], true)) + return true; + + switch (RV.getReflectionKind()) { + case ReflectionKind::Null: + case ReflectionKind::Template: + case ReflectionKind::Namespace: + case ReflectionKind::BaseSpecifier: + case ReflectionKind::DataMemberSpec: + case ReflectionKind::Annotation: + case ReflectionKind::Type: + case ReflectionKind::Object: + case ReflectionKind::Value: + return SetAndSucceed(Result, makeBool(C, false)); + case ReflectionKind::Declaration: { + bool result = false; + if (auto *FD = dyn_cast(RV.getReflectedDecl())) + result = FD->isMutable(); + + return SetAndSucceed(Result, makeBool(C, result)); + } + } + llvm_unreachable("invalid reflection type"); +} + bool is_lvalue_reference_qualified(APValue &Result, ASTContext &C, MetaActions &Meta, EvalFn Evaluator, DiagFn Diagnoser, QualType ResultTy, diff --git a/clang/lib/AST/Reflection.cpp b/clang/lib/AST/Reflection.cpp index c55f1b6bb1581d..6a077d0b32e60d 100644 --- a/clang/lib/AST/Reflection.cpp +++ b/clang/lib/AST/Reflection.cpp @@ -17,7 +17,7 @@ namespace clang { bool TagDataMemberSpec::operator==(TagDataMemberSpec const &Rhs) const { - return (Ty == Ty && + return (Ty == Rhs.Ty && Alignment == Rhs.Alignment && BitWidth == Rhs.BitWidth && Name == Rhs.Name); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 6c38df3143e7dd..0c5291eaaeef61 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9044,6 +9044,8 @@ TreeTransform::TransformCXXDependentMemberSpliceExpr( CXXDependentMemberSpliceExpr *E) { ExprResult Base = getDerived().TransformExpr(E->getBase()); ExprResult RHS = getDerived().TransformExpr(E->getRHS()); + if (Base.isInvalid() || RHS.isInvalid()) + return ExprError(); return getSema().BuildMemberReferenceExpr( nullptr, Base.get(), E->getOpLoc(), diff --git a/libcxx/include/experimental/meta b/libcxx/include/experimental/meta index 1a1b322cc88d24..5b8de4821b9550 100644 --- a/libcxx/include/experimental/meta +++ b/libcxx/include/experimental/meta @@ -66,9 +66,15 @@ consteval auto nonstatic_data_members_of(info class_type) -> vector; consteval auto subobjects_of(info class_type) -> vector; consteval auto enumerators_of(info enum_type) -> vector; +// public member queries +consteval auto get_public_members(info) -> vector; +consteval auto get_public_bases(info) -> vector; +consteval auto get_static_data_members(info) -> vector; +consteval auto get_nonstatic_data_members(info) -> vector; + // member access queries struct access_context { - consteval access_context() = default; + consteval access_context(); consteval access_context(const access_context &) = default; consteval access_context(access_context &&) = default; @@ -137,6 +143,7 @@ consteval auto is_bit_field(info) -> bool; consteval auto is_enumerator(info) -> bool; consteval auto is_const(info) -> bool; consteval auto is_volatile(info) -> bool; +consteval auto is_mutable_member(info) -> bool; consteval auto is_lvalue_reference_qualified(info) -> bool; consteval auto is_rvalue_reference_qualified(info) -> bool; consteval auto has_static_storage_duration(info) -> bool; @@ -362,6 +369,13 @@ template > consteval auto type_unwrap_reference(info) -> info; consteval auto type_unwrap_ref_decay(info) -> info; +// tuple and variant queries +consteval auto type_tuple_size(info) -> size_t; +consteval auto type_tuple_element(size_t, info) -> info; + +consteval auto type_variant_size(info) -> size_t; +consteval auto type_variant_alternative(size_t, info) -> info; + // function parameters (P3096) consteval auto parameters_of(info) -> vector; consteval auto has_consistent_identifier(info) -> bool; @@ -463,6 +477,7 @@ enum : unsigned { __metafn_is_enumerator, __metafn_is_const, __metafn_is_volatile, + __metafn_is_mutable_member, __metafn_is_lvalue_reference_qualified, __metafn_is_rvalue_reference_qualified, __metafn_has_static_storage_duration, @@ -778,16 +793,16 @@ consteval auto u8display_string_of(info r) -> u8string_view; // Enumeration of all overloadable operators. enum class operators { - new_object = 1, delete_object, new_array, delete_array, coroutine_await, - parentheses, square_brackets, arrow, arrow_asterisk, tilde, - exclamation_mark, plus, minus, asterisk, solidus, percent, - caret, ampersand, pipe, equals, plus_equals, minus_equals, - asterisk_equals, solidus_equals, percent_equals, caret_equals, - ampersand_equals, pipe_equals, equals_equals, exclamation_equals, - less, greater, less_equals, greater_equals, three_way_compare, - ampersand_ampersand, pipe_pipe, less_less, greater_greater, - less_less_equals, greater_greater_equals, plus_plus, minus_minus, - comma, + op_new = 1, op_delete, op_array_new, op_array_delete, op_co_await, + op_parentheses, op_square_brackets, op_arrow, op_arrow_star, op_tilde, + op_exclaim, op_plus, op_minus, op_star, op_slash, op_percent, op_caret, + op_ampersand, op_pipe, op_equals, op_plus_equals, op_minus_equals, + op_star_equals, op_slash_equals, op_percent_equals, op_caret_equals, + op_ampersand_equals, op_pipe_equals, op_equals_equals, op_exclaim_equals, + op_less, op_greater, op_less_equals, op_greater_equals, op_three_way_compare, + op_ampersand_ampersand, op_pipe_pipe, op_less_less, op_greater_greater, + op_less_less_equals, op_greater_greater_equals, op_plus_plus, op_minus_minus, + op_comma, }; // Returns the operator overloaded by the represented operator function or @@ -801,6 +816,29 @@ consteval auto has_identifier(info r) -> bool { return __metafunction(detail::__metafn_has_identifier, r); } +// Returns the string representation of the operator. +consteval auto operator_symbol_of(operators op) -> string_view { + static constexpr string_view op_names[45] = { + {}, "new", "delete", "new[]", "delete[]", "coawait", "()", "[]", "->", + "->*", "~", "!", "+", "-", "*", "/", "%", "^", "&", "|", "=", "+=", "-=", + "*=", "/=", "%=", "^", "&=", "|=", "==", "!=", "<", ">", "<=", ">=", + "<=>", "&&", "||", "<<", ">>", "<<=", ">>=", "++", "--", "," + }; + return op_names[int(op)]; +} + +consteval auto u8operator_symbol_of(operators op) -> u8string_view { + static constexpr u8string_view op_names[45] = { + {}, u8"new", u8"delete", u8"new[]", u8"delete[]", u8"coawait", u8"()", + u8"[]", u8"->", u8"->*", u8"~", u8"!", u8"+", u8"-", u8"*", u8"/", u8"%", + u8"^", u8"&", u8"|", u8"=", u8"+=", u8"-=", u8"*=", u8"/=", u8"%=", + u8"^", u8"&=", u8"|=", u8"==", u8"!=", u8"<", u8">", u8"<=", u8">=", + u8"<=>", u8"&&", u8"||", u8"<<", u8">>", u8"<<=", u8">>=", u8"++", + u8"--", u8"," + }; + return op_names[int(op)]; +} + // Returns the source location of the reflected entity. consteval auto source_location_of(info r) -> source_location { auto ptr = __metafunction(detail::__metafn_source_location_of, r); @@ -947,13 +985,36 @@ consteval auto enumerators_of(info r) -> vector { return vector{rng.begin(), rng.end()}; } +// Returns whether the reflected entity is a public class member. +consteval auto is_public(info r) -> bool { + return __metafunction(detail::__metafn_is_public, r); +} + +consteval auto get_public_members(info r) -> vector { + return members_of(r) | views::filter(is_public) | ranges::to(); +} + +consteval auto get_public_bases(info r) -> vector { + return bases_of(r) | views::filter(is_public) | ranges::to(); +} + +consteval auto get_public_static_data_members(info r) -> vector { + return static_data_members_of(r) | views::filter(is_public) | + ranges::to(); +} + +consteval auto get_public_nonstatic_data_members(info r) -> vector { + return nonstatic_data_members_of(r) | views::filter(is_public) | + ranges::to(); +} + class access_context { const info repr; consteval access_context(info repr) : repr(repr) { } public: - consteval access_context() noexcept : repr() { }; + consteval access_context() noexcept : repr(LIFT(::)) { }; consteval access_context(const access_context &) noexcept = default; consteval access_context(access_context &&) noexcept = default; @@ -1061,11 +1122,6 @@ consteval auto extract(info r) -> Ty { return __metafunction(detail::__metafn_extract, LIFT(Ty), r); } -// Returns whether the reflected entity is a public class member. -consteval auto is_public(info r) -> bool { - return __metafunction(detail::__metafn_is_public, r); -} - // Returns whether the reflected entity is a protected class member. consteval auto is_protected(info r) -> bool { return __metafunction(detail::__metafn_is_protected, r); @@ -1141,6 +1197,11 @@ consteval auto is_volatile(info r) -> bool { return __metafunction(detail::__metafn_is_volatile, r); } +// Returns whether the reflected entity is a mutable class member. +consteval auto is_mutable_member(info r) -> bool { + return __metafunction(detail::__metafn_is_mutable_member, r); +} + // Returns whether the reflected member function or function type is // lvalue-reference qualified. consteval auto is_lvalue_reference_qualified(info r) -> bool { @@ -2071,6 +2132,25 @@ consteval auto type_unwrap_ref_decay(info type) -> info { return dealias(substitute(LIFT(unwrap_ref_decay_t), {type})); } +consteval auto type_tuple_size(info type) -> size_t { + return extract(substitute(LIFT(tuple_size_v), {type})); +} + +consteval auto type_tuple_element(size_t index, info type) -> info { + return dealias(substitute(LIFT(tuple_element_t), + {std::meta::reflect_value(index), type})); +} + +consteval auto type_variant_size(info type) -> size_t { + return extract(substitute(LIFT(variant_size_v), {type})); +} + +consteval auto type_variant_alternative(size_t index, info type) -> info { + return dealias(substitute(LIFT(variant_alternative_t), + {std::meta::reflect_value(index), type})); +} + + namespace detail { template struct __wrap_workaround { using type = T; }; consteval auto __workaround_expand_compiler_builtins(info type) -> info { @@ -2246,25 +2326,6 @@ struct pretty_printer { } } - static constexpr basic_string_view op_names[45] = { - {}, string_constant("new"), string_constant("delete"), - string_constant("new[]"), string_constant("delete[]"), - string_constant("coawait"), string_constant("()"), string_constant("[]"), - string_constant("->"), string_constant("->*"), string_constant("~"), - string_constant("!"), string_constant("+"), string_constant("-"), - string_constant("*"), string_constant("/"), string_constant("%"), - string_constant("^"), string_constant("&"), string_constant("|"), - string_constant("="), string_constant("+="), string_constant("-="), - string_constant("*="), string_constant("/="), string_constant("%="), - string_constant("^"), string_constant("&="), string_constant("|="), - string_constant("=="), string_constant("!="), string_constant("<"), - string_constant(">"), string_constant("<="), string_constant(">="), - string_constant("<=>"), string_constant("&&"), string_constant("||"), - string_constant("<<"), string_constant(">>"), string_constant("<<="), - string_constant(">>="), string_constant("++"), string_constant("--"), - string_constant(","), - }; - static consteval auto identifier_helper(info R) { if constexpr (IsUtf8) return u8identifier_of(R); @@ -2272,6 +2333,13 @@ struct pretty_printer { return identifier_of(R); } + static consteval auto operator_symbol_helper(operators Op) { + if constexpr (IsUtf8) + return u8operator_symbol_of(Op); + else + return operator_symbol_of(Op); + } + template static consteval info type_of_object_from_memptr() { return []::tprint_impl::render() -> string_t { string_t result = "operator"; switch (op) { - case std::meta::operators::new_object: - case std::meta::operators::delete_object: - case std::meta::operators::new_array: - case std::meta::operators::delete_array: - case std::meta::operators::coroutine_await: + case std::meta::operators::op_new: + case std::meta::operators::op_delete: + case std::meta::operators::op_array_new: + case std::meta::operators::op_array_delete: + case std::meta::operators::op_co_await: result += " "; [[fallthrough]]; default: - return result + string_t {op_names[int(operator_of(R))]}; + return result + operator_symbol_helper(op); } } else if (is_conversion_function_template(R)) return string_constant("(conversion-function-template)"); @@ -2600,15 +2668,15 @@ consteval auto pretty_printer::tprint_impl::render() -> string_t { result = "operator"; switch (op) { - case std::meta::operators::new_object: - case std::meta::operators::delete_object: - case std::meta::operators::new_array: - case std::meta::operators::delete_array: - case std::meta::operators::coroutine_await: + case std::meta::operators::op_new: + case std::meta::operators::op_delete: + case std::meta::operators::op_array_new: + case std::meta::operators::op_array_delete: + case std::meta::operators::op_co_await: result += " "; [[fallthrough]]; default: - return result + string_t {op_names[int(operator_of(R))]}; + return result + operator_symbol_helper(op); } } else if constexpr (is_conversion_function(R)) { #if __has_feature(parameter_reflection) diff --git a/libcxx/test/std/experimental/reflection/member-classification.pass.cpp b/libcxx/test/std/experimental/reflection/member-classification.pass.cpp index 80c3aaedac1cdc..e980cb6b48a586 100644 --- a/libcxx/test/std/experimental/reflection/member-classification.pass.cpp +++ b/libcxx/test/std/experimental/reflection/member-classification.pass.cpp @@ -1020,4 +1020,21 @@ static_assert(is_literal_operator_template(^^operator""_b)); } // namespace operators_and_conversion_functions + // =============== + // mutable_members + // =============== + +namespace mutable_members { +struct S { + int a; + mutable int b; + void fn(); +}; + +static_assert(!is_mutable_member(^^S)); +static_assert(!is_mutable_member(^^S::a)); +static_assert(is_mutable_member(^^S::b)); +static_assert(!is_mutable_member(^^S::fn)); +} // namespace mutable_members + int main() { } diff --git a/libcxx/test/std/experimental/reflection/member-visibility.pass.cpp b/libcxx/test/std/experimental/reflection/member-visibility.pass.cpp index 824aefe36f3e36..9e7e18d1b84498 100644 --- a/libcxx/test/std/experimental/reflection/member-visibility.pass.cpp +++ b/libcxx/test/std/experimental/reflection/member-visibility.pass.cpp @@ -198,12 +198,15 @@ consteval std::meta::access_context FriendFnOfAccess() { // new_accessibility_api // ===================== + +static_assert(get_ctx_repr(std::meta::access_context{}) == ^^::); static_assert(get_ctx_repr(std::meta::access_context::current()) == ^^::); namespace new_accessibility_api { static_assert(get_ctx_repr(std::meta::access_context::current()) == ^^::new_accessibility_api); void fn() { + static_assert(get_ctx_repr(std::meta::access_context{}) == ^^::); static_assert(get_ctx_repr(std::meta::access_context::current()) == ^^fn); [] { constexpr auto repr = get_ctx_repr(std::meta::access_context::current()); diff --git a/libcxx/test/std/experimental/reflection/meta-type-traits.pass.cpp b/libcxx/test/std/experimental/reflection/meta-type-traits.pass.cpp index bea62fe7e4cfac..09a96fe6710737 100644 --- a/libcxx/test/std/experimental/reflection/meta-type-traits.pass.cpp +++ b/libcxx/test/std/experimental/reflection/meta-type-traits.pass.cpp @@ -218,5 +218,12 @@ static_assert( type_unwrap_ref_decay(^^std::reference_wrapper)) == display_string_of(^^const int &)); +using Tup = std::tuple; +static_assert(type_tuple_size(^^Tup) == 3); +static_assert(type_tuple_element(1, ^^Tup) == ^^bool); + +using Var = std::variant; +static_assert(type_variant_size(^^Var) == 4); +static_assert(type_variant_alternative(3, ^^Var) == ^^int *); int main() { } diff --git a/libcxx/test/std/experimental/reflection/p2996-ex-tuple-cat.pass.cpp b/libcxx/test/std/experimental/reflection/p2996-ex-tuple-cat.pass.cpp index 8d01e75c68798d..75c4f3a5290afd 100644 --- a/libcxx/test/std/experimental/reflection/p2996-ex-tuple-cat.pass.cpp +++ b/libcxx/test/std/experimental/reflection/p2996-ex-tuple-cat.pass.cpp @@ -28,12 +28,6 @@ #include -namespace std::meta { - consteval auto type_tuple_size(info type) -> size_t { - return extract(substitute(^^std::tuple_size_v, {type})); - } -} - template... indices> struct Indexer { template diff --git a/libcxx/test/std/experimental/reflection/substitute.pass.cpp b/libcxx/test/std/experimental/reflection/substitute.pass.cpp index 722946f1080e16..b3f64898bd1f88 100644 --- a/libcxx/test/std/experimental/reflection/substitute.pass.cpp +++ b/libcxx/test/std/experimental/reflection/substitute.pass.cpp @@ -424,4 +424,17 @@ static_assert(!can_substitute(^^Cls, {^^int, ^^bool, ^^std::array, ^^std::array})); } // namespace invalid_template_ids + // ======================================== + // bb_clang_p2996_issue_101_regression_test + // ======================================== + +namespace bb_clang_p2996_issue_101_regression_test { +template using member_pointer = T C::*; + +struct Test { }; +static_assert(substitute(^^member_pointer, {^^Test, ^^int}) != + substitute(^^member_pointer, {^^Test, ^^float})); +} // namespace bb_clang_p2996_issue_101_regression_test + + int main() { }