On 6/24/25 2:25 PM, Jakub Jelinek wrote:
On Tue, Jun 24, 2025 at 11:57:01AM -0400, Jason Merrill wrote:
The two other errors on the testcase are expectedly gone with C++26,
but the last one remains.  The problem is that when parsing nm3.a
inside of mutable_subobjects()::A::f()
build_class_member_access_expr calls build_base_path which calls
cp_build_addr_expr and that makes nm3 odr-used.  I must say I have
no idea whether nm3 ought to be odr-used or not just because of nm3.a
use and if not, how that should be changed.

build_simple_base_path is how we avoid this odr-use; seems we also need to
use it early in the case of (v_binfo && !virtual_access).

--- gcc/cp/class.cc.jj  2025-06-18 17:24:03.973867379 +0200
+++ gcc/cp/class.cc     2025-06-24 20:11:21.728169508 +0200
@@ -349,7 +349,11 @@ build_base_path (enum tree_code code,
/* For a non-pointer simple base reference, express it as a COMPONENT_REF
       without taking its address (and so causing lambda capture, 91933).  */
-  if (code == PLUS_EXPR && !v_binfo && !want_pointer && !has_empty && !uneval)
+  if (code == PLUS_EXPR
+      && !want_pointer
+      && !has_empty
+      && !uneval
+      && (!v_binfo || resolves_to_fixed_type_p (expr) > 0))
      return build_simple_base_path (expr, binfo);
if (!want_pointer)

seems to fix that and doesn't regress anything else in make check-g++.
I guess it can be handled separately from the rest.
Or do you prefer some other way to avoid calling resolves_to_fixed_type_p
twice in some cases?

I think we could move the initialization of the fixed_type_p and virtual_access variables up, they don't need to be after cp_build_addr_expr.

works at runtime.  In the patch I've adjusted the function
comment of cxx_eval_dynamic_cast_fn because with virtual bases
I believe hint -1 might be possible, though I'm afraid I don't

Yes, we would get -1 for dynamic_cast from B to A.

The routine then has some
   /* Given dynamic_cast<T>(v),

      [expr.dynamic.cast] If C is the class type to which T points or refers,
      the runtime check logically executes as follows:

      If, in the most derived object pointed (referred) to by v, v points
      (refers) to a public base class subobject of a C object, and if only
      one object of type C is derived from the subobject pointed (referred)
      to by v the result points (refers) to that C object.

      In this case, HINT >= 0 or -3.  */
   if (hint >= 0 || hint == -3)
Should that include the hint == -1 case too (so effectively if (hint != -2)
or is -1 not relevant to that block.

I think -1 doesn't distinguish between single or multiple virtual derivation, so handling -1 in that block might mean succeeding for a multiple derivation case where it ought to fail.

know enough about dynamic_cast and cxx_eval_dynamic_cast_fn
to figure out what needs to change there.  It is hint -2 that
fails, not hint -1.

Yes, this is a -2 case because C does not derive from B.

How does cxx_eval_dynamic_cast_fn fail in this case?  From looking at the
function it seems like it ought to work.
>>...
Actually, I see the reason now.
get_component_path is called with
a.D.2692, A, NULL
where a.D.2692 has B type.
And the reason why it fails is
2538              /* We need to check that the component we're accessing is in 
fact
2539                 accessible.  */
2540              if (TREE_PRIVATE (TREE_OPERAND (path, 1))
2541                  || TREE_PROTECTED (TREE_OPERAND (path, 1)))
2542                return error_mark_node;
The D.2692 FIELD_DECL has been created by build_base_field_1
called from build_base_field from layout_virtual_bases and that one calls it
with
6753          if (!BINFO_PRIMARY_P (vbase))
6754            {
6755              /* This virtual base is not a primary base of any class in the
6756                 hierarchy, so we have to add space for it.  */
6757              next_field = build_base_field (rli, vbase,
6758                                             access_private_node,
6759                                             offsets, next_field);
6760            }
access_private_node forces TREE_PRIVATE on the FIELD_DECL and so it doesn't
reflect whether the base in question was private/protected or public.
struct A has also D.2689 FIELD_DECL with C type and that one is the primary
base, neither TREE_PRIVATE nor TREE_PROTECTED.

So, shall I e.g. for the if (TREE_PRIVATE case if the outer type has
CLASSTYPE_VBASECLASSES walk the
for (vbase = TYPE_BINFO (t); vbase; vbase = TREE_CHAIN (vbase))
if (BINFO_VIRTUAL_P (vbase) && !BINFO_PRIMARY_P (vbase))
and in that case try to compare byte_position (TREE_OPERAND (path, 1))
against BINFO_OFFSET (vbase) and if it matches (plus perhaps some type
check?) then decide based on BINFO_BASE_ACCESS or something like that
whether it was a private/protected vs. public virtual base?

It seems simpler to pass an accurate access to the build_base_field above. At least whether the whole BINFO_INHERITANCE_CHAIN is public or not, I suppose the distinction between private and protected doesn't matter.

Jason

Reply via email to