The following test case highlights two issues - see https://godbolt.org/z/7E1KGYreh: 1. We error out at both L4 and L5, while (at least) clang, EDG and MSVC only reject L5 2. Our error message for L5 incorrectly mentions using a null pointer
=== cut here === struct A { int i; }; struct C : virtual public A { }; void foo () { auto res = ((C*)0)->i; // L4 __builtin_offsetof (C, i); // L5 } === cut here === I am not aware of anything in the standard that'd make L4 invalid, and this patch aligns our behaviour with that of other compilers by removing the check for a null instance (that was primarily targetting offsetof according to its comment) from build_class_member_access_expr, and having finish_offsetof check and report accesses to members from a virtual base class. Successfully tested on x86_64-pc-linux-gnu. PR c++/118346 gcc/cp/ChangeLog: * semantics.cc (finish_offsetof): Reject accesses to a virtual base member. * typeck.cc (build_class_member_access_expr): Don't reject virtual base member access of null objects. gcc/testsuite/ChangeLog: * g++.dg/other/offsetof8.C: Add untested scenarios. * g++.dg/other/offsetof9.C: Adjust test expectations. --- gcc/cp/semantics.cc | 28 ++++++++++++++++++------ gcc/cp/typeck.cc | 20 ----------------- gcc/testsuite/g++.dg/other/offsetof8.C | 30 +++++++++++++++++++++----- gcc/testsuite/g++.dg/other/offsetof9.C | 6 +++--- 4 files changed, 50 insertions(+), 34 deletions(-) diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 43a0eabfa12..9ba00196542 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -5324,13 +5324,29 @@ finish_offsetof (tree object_ptr, tree expr, location_t loc) expr = TREE_OPERAND (expr, 0); if (!complete_type_or_else (TREE_TYPE (TREE_TYPE (object_ptr)), object_ptr)) return error_mark_node; + if (warn_invalid_offsetof - && CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (object_ptr))) - && CLASSTYPE_NON_STD_LAYOUT (TREE_TYPE (TREE_TYPE (object_ptr))) - && cp_unevaluated_operand == 0) - warning_at (loc, OPT_Winvalid_offsetof, "%<offsetof%> within " - "non-standard-layout type %qT is conditionally-supported", - TREE_TYPE (TREE_TYPE (object_ptr))); + && CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (object_ptr)))) + { + bool member_in_base_p = false; + if (TREE_CODE (expr) == COMPONENT_REF) + { + tree member = expr; + do { + member = TREE_OPERAND (member, 1); + } while (TREE_CODE (member) == COMPONENT_REF); + member_in_base_p = !same_type_p (TREE_TYPE (TREE_TYPE (object_ptr)), + DECL_CONTEXT (member)); + } + if (member_in_base_p + && CLASSTYPE_VBASECLASSES (TREE_TYPE (TREE_TYPE (object_ptr)))) + error_at (loc, "invalid %<offsetof%> to data member in virtual base"); + else if (CLASSTYPE_NON_STD_LAYOUT (TREE_TYPE (TREE_TYPE (object_ptr))) + && cp_unevaluated_operand == 0) + warning_at (loc, OPT_Winvalid_offsetof, "%<offsetof%> within " + "non-standard-layout type %qT is conditionally-supported", + TREE_TYPE (TREE_TYPE (object_ptr))); + } return fold_offsetof (expr); } diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 1b9fdf5b21d..4e5d0507493 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -2926,16 +2926,9 @@ build_class_member_access_expr (cp_expr object, tree member, else if (TREE_CODE (member) == FIELD_DECL) { /* A non-static data member. */ - bool null_object_p; int type_quals; tree member_type; - if (INDIRECT_REF_P (object)) - null_object_p = - integer_zerop (tree_strip_nop_conversions (TREE_OPERAND (object, 0))); - else - null_object_p = false; - /* Convert OBJECT to the type of MEMBER. */ if (!same_type_p (TYPE_MAIN_VARIANT (object_type), TYPE_MAIN_VARIANT (member_scope))) @@ -2957,19 +2950,6 @@ build_class_member_access_expr (cp_expr object, tree member, if (binfo == error_mark_node) return error_mark_node; - /* It is invalid to try to get to a virtual base of a - NULL object. The most common cause is invalid use of - offsetof macro. */ - if (null_object_p && kind == bk_via_virtual) - { - if (complain & tf_error) - { - error ("invalid access to non-static data member %qD in " - "virtual base of NULL object", member); - } - return error_mark_node; - } - /* Convert to the base. */ object = build_base_path (PLUS_EXPR, object, binfo, /*nonnull=*/1, complain); diff --git a/gcc/testsuite/g++.dg/other/offsetof8.C b/gcc/testsuite/g++.dg/other/offsetof8.C index daca70a6fe4..96f1507b845 100644 --- a/gcc/testsuite/g++.dg/other/offsetof8.C +++ b/gcc/testsuite/g++.dg/other/offsetof8.C @@ -1,12 +1,32 @@ // PR c++/68711 - [5 regression] SEGV on an invalid offsetof of a member // of a virtual base +// PR c++/118346 // { dg-do compile } -struct A { int i; }; +struct A { + int i; + static int s; +}; + +struct B: virtual A { int j; }; + +long unsigned a[] = { + // Non static data member in virtual base. + !&((B*)0)->i, + __builtin_offsetof (B, i), // { dg-error "data member in virtual base" } + + // Non static data member in class with virtual base. + !&((B*)0)->j, + __builtin_offsetof (B, j), // { dg-warning "within non-standard-layout type" } -struct B: virtual A { }; + // Static data member in virtual base. + !&((B*)0)->s, + __builtin_offsetof (B, s), // { dg-error "to static data" } + // { dg-warning "within non-standard-layout type" "" { target *-*-* } .-1 } -int a[] = { - !&((B*)0)->i, // { dg-error "invalid access to non-static data member" } - __builtin_offsetof (B, i) // { dg-error "invalid access to non-static" } + // No virtual base involvement. + !&((A*)0)->i, + __builtin_offsetof (A, i), + !&((A*)0)->s, + __builtin_offsetof (A, s) // { dg-error "to static data" } }; diff --git a/gcc/testsuite/g++.dg/other/offsetof9.C b/gcc/testsuite/g++.dg/other/offsetof9.C index 1936f2e5f76..ac07c21ef34 100644 --- a/gcc/testsuite/g++.dg/other/offsetof9.C +++ b/gcc/testsuite/g++.dg/other/offsetof9.C @@ -4,14 +4,14 @@ struct A { int i; }; struct B : virtual A { }; -__SIZE_TYPE__ s = __builtin_offsetof (B, A::i); // { dg-warning "'offsetof' within non-standard-layout type" } +__SIZE_TYPE__ s = __builtin_offsetof (B, A::i); // { dg-error "'offsetof' to data member in virtual base" } template <typename T> __SIZE_TYPE__ foo () { - return __builtin_offsetof (T, A::i) // { dg-warning "'offsetof' within non-standard-layout type" } - + __builtin_offsetof (B, A::i); // { dg-warning "'offsetof' within non-standard-layout type" } + return __builtin_offsetof (T, A::i) // { dg-error "'offsetof' to data member in virtual base" } + + __builtin_offsetof (B, A::i); // { dg-error "'offsetof' to data member in virtual base" } } __SIZE_TYPE__ t = foo<B> (); -- 2.44.0