On Tue, Jun 24, 2025 at 05:19:59PM -0400, Jason Merrill wrote:
> 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.

I don't understand why it doesn't depend on cp_build_addr_expr.
I've tried the following patch and while it didn't regress anything on
make GXX_TESTSUITE_STDS=98,11,14,17,^C,23,26 check-g++
it regressed
FAIL: 23_containers/vector/bool/cmp_c++20.cc  -std=gnu++20 (test for excess 
errors)
FAIL: 23_containers/vector/bool/cmp_c++20.cc  -std=gnu++26 (test for excess 
errors)
In there code is PLUS_EXPR, !want_pointer, !has_empty, but uneval is true
and expr is
std::vector<bool>::begin (&c)
before cp_build_addr_expr and
&TARGET_EXPR <D.316846, std::vector<bool>::begin (&c)>
after it.  resolves_to_fixed_type_p (expr) is 0 before cp_build_addr_expr
and 1 after it.  v_binfo is false though, so in that
particular case I think we don't actually care about fixed_type_p value,
but it doesn't raise confidence that testing resolves_to_fixed_type_p
early is ok.

--- gcc/cp/class.cc.jj  2025-06-18 17:24:03.973867379 +0200
+++ gcc/cp/class.cc     2025-06-25 08:01:06.824278658 +0200
@@ -347,9 +347,19 @@ build_base_path (enum tree_code code,
                 || processing_template_decl
                 || in_template_context);
 
+  int nonnull_copy = nonnull;
+  fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull);
+
+  /* Do we need to look in the vtable for the real offset?  */
+  virtual_access = (v_binfo && fixed_type_p <= 0);
+
   /* 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 || !virtual_access))
     return build_simple_base_path (expr, binfo);
 
   if (!want_pointer)
@@ -361,8 +371,10 @@ build_base_path (enum tree_code code,
   else
     expr = mark_rvalue_use (expr);
 
+  gcc_assert (resolves_to_fixed_type_p (expr, &nonnull_copy)
+             == fixed_type_p && nonnull_copy == nonnull);
+
   offset = BINFO_OFFSET (binfo);
-  fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull);
   target_type = code == PLUS_EXPR ? BINFO_TYPE (binfo) : BINFO_TYPE (d_binfo);
   /* TARGET_TYPE has been extracted from BINFO, and, is therefore always
      cv-unqualified.  Extract the cv-qualifiers from EXPR so that the
@@ -371,9 +383,6 @@ build_base_path (enum tree_code code,
     (target_type, cp_type_quals (TREE_TYPE (TREE_TYPE (expr))));
   ptr_target_type = build_pointer_type (target_type);
 
-  /* Do we need to look in the vtable for the real offset?  */
-  virtual_access = (v_binfo && fixed_type_p <= 0);
-
   /* Don't bother with the calculations inside sizeof; they'll ICE if the
      source type is incomplete and the pointer value doesn't matter.  In a
      template (even in instantiate_non_dependent_expr), we don't have vtables


> 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.

Ok, will keep it as is then.

> > 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.

I'm afraid I'm quite lost on what actually is public base class
that [expr.dynamic.cast] talks about in the case of virtual bases because
a virtual base can appear many times among the bases and if it is virtual
in all cases, there is just one copy of it and it can be public in some
paths and private/protected in others.  And where to find that information.

I've tried the following testcase and it seems that it succeeds unless
-DP1 -DP2
-DP1 -DP3
-DP1 -DP6
-DP2 -DP3 -DP6
-DP4 -DP5 -DP6
-DP2 -DP3 -DP4 -DP5
is a subset of the -DPN options or in case of clang++ also
-DP2 -DP4 -DP5 (for that g++ passes, clang++ fails).
E.g. what is the difference between -DP1 which works and
S is private in one case and public in 2 others, while -DP1 -DP2
doesn't work and is private in two cases and public in one.

#ifdef P1
#undef P1
#define P1 private
#else
#define P1
#endif
#ifdef P2
#undef P2
#define P2 private
#else
#define P2
#endif
#ifdef P3
#undef P3
#define P3 private
#else
#define P3
#endif
#ifdef P4
#undef P4
#define P4 private
#else
#define P4
#endif
#ifdef P5
#undef P5
#define P5 private
#else
#define P5
#endif
#ifdef P6
#undef P6
#define P6 private
#else
#define P6
#endif
struct S { int a, b; virtual int bar (int) { return 0; } };
struct T : virtual P1 S { int c, d; };
struct U : virtual P2 S, virtual P3 T { int e; };
struct V : virtual P4 S, virtual P5 T, virtual P6 U { int f; S &foo () { return 
(S &)*this; } };

int
main ()
{
  V t;
  t.f = 1;
//  t.c = 2;
  dynamic_cast<V &> (t.foo ());
  dynamic_cast<T &> (t.foo ());
  dynamic_cast<U &> (t.foo ());
}

I've tried to look at BINFO_INHERITANCE_CHAIN in the debugger and see
on this testcase 6 calls of
          /* This virtual base is not a primary base of any class in the
             hierarchy, so we have to add space for it.  */
          next_field = build_base_field (rli, vbase,
                                         access_private_node,
                                         offsets, next_field);
(for S in T, for S and T in U and for S, T and U in V) and
vbase->binfo.inheritance->binfo.base_accesses
are in all those cases vectors of private/public in the particular
class (so P1 in the first case, P2 and P3 in the second and third
and P4, P5 and P6 in the last 3), but from that alone one can't
judge whether dynamic_cast will work or not.

What exactly does dynamic_cast look at in the vtables to decide
if it is the public or non-public case?  Or could we instead of
checking for TREE_PUBLIC/TREE_PRIVATE on the DECL_FIELD_IS_BASE
FIELD_DECLs try to tf_none implicitly convert the FIELD_DECL
type to the COMPONENT_REFs first operand's type?

        Jakub

Reply via email to