aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/tree.c
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2021-07-30 18:38:41 +0200
committerJakub Jelinek <jakub@redhat.com>2021-07-30 18:38:41 +0200
commit6cd005a255f15c1b4b3eaae71c844ea2592c9dce (patch)
tree650f245a19c1a68b5f04a7550e1fe409d635299a /gcc/cp/tree.c
parent3ead06c1cff8fb42b4e278c3624917e6b5477f12 (diff)
downloadgcc-6cd005a255f15c1b4b3eaae71c844ea2592c9dce.zip
gcc-6cd005a255f15c1b4b3eaae71c844ea2592c9dce.tar.gz
gcc-6cd005a255f15c1b4b3eaae71c844ea2592c9dce.tar.bz2
c++: Implement P0466R5 __cpp_lib_is_pointer_interconvertible compiler helpers [PR101539]
The following patch attempts to implement the compiler helpers for libstdc++ std::is_pointer_interconvertible_base_of trait and std::is_pointer_interconvertible_with_class template function. For the former __is_pointer_interconvertible_base_of trait that checks first whether base and derived aren't non-union class types that are the same ignoring toplevel cv-qualifiers, otherwise if derived is unambiguously derived from base without cv-qualifiers, derived being a complete type, and if so, my limited understanding of any derived object being pointer-interconvertible with base subobject IMHO implies (because one can't inherit from unions or unions can't inherit) that we check if derived is standard layout type and we walk bases of derived recursively, stopping on a class that has any non-static data members and check if any of the bases is base. On class with non-static data members no bases are compared already. Upon discussions, this is something that maybe should have been changed in the standard with CWG 2254 and the patch no longer performs this and assumes all base subobjects of standard-layout class types are pointer-interconvertible with the whole class objects. The latter is implemented using a FE __builtin_is_pointer_interconvertible_with_class, but because on the library side it will be a template function, the builtin takes ... arguments and only during folding verifies it has a single argument with pointer to member type. The initial errors IMHO can only happen if one uses the builtin incorrectly by hand, the template function should ensure that it has exactly a single argument that has pointer to member type. Otherwise, again with my limited understanding of what the template function should do and pointer-interconvertibility, it folds to false for pointer-to-member-function, errors if basetype of the OFFSET_TYPE is incomplete, folds to false for non-std-layout non-union basetype, then finds the first non-static data member in the basetype or its bases (by ignoring DECL_FIELD_IS_BASE FIELD_DECLs that are empty, recursing into DECL_FIELD_IS_BASE FIELD_DECLs type that are non-empty (I think std layout should ensure there is at most one), for unions checks if membertype is same type as any of the union FIELD_DECLs, for non-unions the first other FIELD_DECL only, and for anonymous aggregates similarly (union vs. non-union) but recurses into the anon aggr types with std layout check for anon structures. If membertype doesn't match the type of first non-static data member (or for unions any of the members), then the builtin folds to false, otherwise the built folds to a check whether the argument is equal to OFFSET_TYPE of 0 or not, either at compile time if it is constant (e.g. for constexpr folding) or at runtime otherwise. As I wrote in the PR, I've tried my testcases with MSVC on godbolt that claims to implement it, and https://godbolt.org/z/3PnjM33vM for the first testcase shows it disagrees with my expectations on static_assert (std::is_pointer_interconvertible_base_of_v<D, F>); static_assert (std::is_pointer_interconvertible_base_of_v<E, F>); static_assert (!std::is_pointer_interconvertible_base_of_v<D, G>); static_assert (!std::is_pointer_interconvertible_base_of_v<D, I>); static_assert (std::is_pointer_interconvertible_base_of_v<H, volatile I>); Is that a bug in my patch or is MSVC buggy on these (or mix thereof)? https://godbolt.org/z/aYeYnne9d shows the second testcase, here it differs on: static_assert (std::is_pointer_interconvertible_with_class<F, int> (&F::b)); static_assert (std::is_pointer_interconvertible_with_class<I, int> (&I::g)); static_assert (std::is_pointer_interconvertible_with_class<L, int> (&L::b)); static_assert (std::is_pointer_interconvertible_with_class (&V::a)); static_assert (std::is_pointer_interconvertible_with_class (&V::b)); Again, my bug, MSVC bug, mix thereof? According to Jason the <D, G>, <D, I> case are the subject of the CWG 2254 above discussed change and the rest are likely MSVC bugs. Oh, and there is another thing, the standard has an example: struct A { int a; }; // a standard-layout class struct B { int b; }; // a standard-layout class struct C: public A, public B { }; // not a standard-layout class static_assert( is_pointer_interconvertible_with_class( &C::b ) ); // Succeeds because, despite its appearance, &C::b has type // “pointer to member of B of type int”. static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); // Forces the use of class C, and fails. It seems to work as written with MSVC (second assertion fails), but fails with GCC with the patch: /tmp/1.C:22:57: error: no matching function for call to ‘is_pointer_interconvertible_with_class<C>(int B::*)’ 22 | static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ /tmp/1.C:8:1: note: candidate: ‘template<class S, class M> constexpr bool std::is_pointer_interconvertible_with_class(M S::*)’ 8 | is_pointer_interconvertible_with_class (M S::*m) noexcept | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /tmp/1.C:8:1: note: template argument deduction/substitution failed: /tmp/1.C:22:57: note: mismatched types ‘C’ and ‘B’ 22 | static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~ the second int argument isn't deduced. This boils down to: template <class S, class M> bool foo (M S::*m) noexcept; struct A { int a; }; struct B { int b; }; struct C : public A, public B {}; bool a = foo (&C::b); bool b = foo<C, int> (&C::b); bool c = foo<C> (&C::b); which with /std:c++20 or -std=c++20 is accepted by latest MSVC and ICC but rejected by GCC and clang (in both cases on the last line). Is this a GCC/clang bug in argument deduction (in that case I think we want a separate PR), or a bug in ICC/MSVC and the standard itself that should specify in the examples both template arguments instead of just the first? And this has been raised with the CWG. 2021-07-30 Jakub Jelinek <jakub@redhat.com> PR c++/101539 gcc/c-family/ * c-common.h (enum rid): Add RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF. * c-common.c (c_common_reswords): Add __is_pointer_interconvertible_base_of. gcc/cp/ * cp-tree.h (enum cp_trait_kind): Add CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF. (enum cp_built_in_function): Add CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS. (fold_builtin_is_pointer_inverconvertible_with_class): Declare. * parser.c (cp_parser_primary_expression): Handle RID_IS_POINTER_INTERCONVERTIBLE_BASE_OF. (cp_parser_trait_expr): Likewise. * cp-objcp-common.c (names_builtin_p): Likewise. * constraint.cc (diagnose_trait_expr): Handle CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF. * decl.c (cxx_init_decl_processing): Register __builtin_is_pointer_interconvertible_with_class builtin. * constexpr.c (cxx_eval_builtin_function_call): Handle CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS builtin. * semantics.c (pointer_interconvertible_base_of_p, first_nonstatic_data_member_p, fold_builtin_is_pointer_inverconvertible_with_class): New functions. (trait_expr_value): Handle CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF. (finish_trait_expr): Likewise. Formatting fix. * cp-gimplify.c (cp_gimplify_expr): Fold CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS. Call fndecl_built_in_p just once. (cp_fold): Likewise. * tree.c (builtin_valid_in_constant_expr_p): Handle CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS. Call fndecl_built_in_p just once. * cxx-pretty-print.c (pp_cxx_trait_expression): Handle CPTK_IS_POINTER_INTERCONVERTIBLE_BASE_OF. gcc/testsuite/ * g++.dg/cpp2a/is-pointer-interconvertible-base-of1.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class1.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class2.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class3.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class4.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class5.C: New test. * g++.dg/cpp2a/is-pointer-interconvertible-with-class6.C: New test.
Diffstat (limited to 'gcc/cp/tree.c')
-rw-r--r--gcc/cp/tree.c15
1 files changed, 10 insertions, 5 deletions
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 2a14fa9..8345396 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -450,11 +450,16 @@ builtin_valid_in_constant_expr_p (const_tree decl)
return false;
if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL)
{
- if (fndecl_built_in_p (decl, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
- BUILT_IN_FRONTEND)
- || fndecl_built_in_p (decl, CP_BUILT_IN_SOURCE_LOCATION,
- BUILT_IN_FRONTEND))
- return true;
+ if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
+ switch (DECL_FE_FUNCTION_CODE (decl))
+ {
+ case CP_BUILT_IN_IS_CONSTANT_EVALUATED:
+ case CP_BUILT_IN_SOURCE_LOCATION:
+ case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
+ return true;
+ default:
+ break;
+ }
/* Not a built-in. */
return false;
}