[PATCH] c++: wrong ambiguity in accessing static field [PR112744]
Jason Merrill
jason@redhat.com
Wed Nov 29 20:28:44 GMT 2023
On 11/29/23 12:43, Marek Polacek wrote:
> On Wed, Nov 29, 2023 at 12:23:46PM -0500, Patrick Palka wrote:
>> On Wed, 29 Nov 2023, Marek Polacek wrote:
>>
>>> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
>>>
>>> Now that I'm posting this patch, I think you'll probably want me to use
>>> ba_any unconditionally. That works too; g++.dg/tc1/dr52.C just needs
>>> a trivial testsuite tweak:
>>> 'C' is not an accessible base of 'X'
>>> v.
>>> 'C' is an inaccessible base of 'X'
>>> We should probably unify those messages...
>>>
>>> -- >8 --
>>> Given
>>>
>>> struct A { constexpr static int a = 0; };
>>> struct B : A {};
>>> struct C : A {};
>>> struct D : B, C {};
>>>
>>> we give the "'A' is an ambiguous base of 'D'" error for
>>>
>>> D{}.A::a;
>>>
>>> which seems wrong: 'a' is a static data member so there is only one copy
>>> so it can be unambiguously referred to even if there are multiple A
>>> objects. clang++/MSVC/icx agree.
>>>
>>> PR c++/112744
>>>
>>> gcc/cp/ChangeLog:
>>>
>>> * typeck.cc (finish_class_member_access_expr): When accessing
>>> a static data member, use ba_any for lookup_base.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> * g++.dg/lookup/scoped11.C: New test.
>>> * g++.dg/lookup/scoped12.C: New test.
>>> * g++.dg/lookup/scoped13.C: New test.
>>> ---
>>> gcc/cp/typeck.cc | 21 ++++++++++++++++++---
>>> gcc/testsuite/g++.dg/lookup/scoped11.C | 14 ++++++++++++++
>>> gcc/testsuite/g++.dg/lookup/scoped12.C | 14 ++++++++++++++
>>> gcc/testsuite/g++.dg/lookup/scoped13.C | 14 ++++++++++++++
>>> 4 files changed, 60 insertions(+), 3 deletions(-)
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/scoped11.C
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/scoped12.C
>>> create mode 100644 gcc/testsuite/g++.dg/lookup/scoped13.C
>>>
>>> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
>>> index e995fb6ddd7..c4de8bb2616 100644
>>> --- a/gcc/cp/typeck.cc
>>> +++ b/gcc/cp/typeck.cc
>>> @@ -3476,7 +3476,7 @@ finish_class_member_access_expr (cp_expr object, tree name, bool template_p,
>>> name, scope);
>>> return error_mark_node;
>>> }
>>> -
>>> +
>>> if (TREE_SIDE_EFFECTS (object))
>>> val = build2 (COMPOUND_EXPR, TREE_TYPE (val), object, val);
>>> return val;
>>> @@ -3493,9 +3493,24 @@ finish_class_member_access_expr (cp_expr object, tree name, bool template_p,
>>> return error_mark_node;
>>> }
>>>
>>> + /* NAME may refer to a static data member, in which case there is
>>> + one copy of the data member that is shared by all the objects of
>>> + the class. So NAME can be unambiguously referred to even if
>>> + there are multiple indirect base classes containing NAME. */
>>> + const base_access ba = [scope, name] ()
>>> + {
>>> + if (identifier_p (name))
>>> + {
>>> + tree m = lookup_member (scope, name, /*protect=*/0,
>>> + /*want_type=*/false, tf_none);
>>> + if (!m || VAR_P (m))
>>> + return ba_any;
>>
>> I wonder if we want to return ba_check_bit instead of ba_any so that we
>> still check access of the selected base?
>
> That would certainly make sense to me. I didn't do that because
> I'd not seen ba_check_bit being used except as part of ba_check,
> but that may not mean much.
>
> So either I can tweak the lambda to return ba_check_bit rather
> than ba_any or use ba_check_bit unconditionally. Any opinions on that?
The relevant passage seems to be
https://eel.is/c++draft/class.access.base#6
after DR 52, which seems to have clarified that the pointer conversion
only applies to non-static members.
>> struct A { constexpr static int a = 0; };
>> struct D : private A {};
>>
>> void f() {
>> D{}.A::a; // #1 GCC (and Clang) currently rejects
>> }
I see that MSVC also rejects it, while EDG accepts.
https://eel.is/c++draft/class.access.base#5.1 seems to say that a is
accessible when named in A.
https://eel.is/c++draft/expr.ref#7 also only constrains references to
non-static members.
But first we need to look up A in D, and A's injected-class-name looked
up as a member of D is not accessible; it's private, and f() is not a
friend, and we correctly complain about that.
If we avoid the lookup of A in D with
D{}.::A::a;
clang accepts it, which is consistent with accepting the template
version, and seems correct.
So, I think ba_any is what we want here.
Jason
More information about the Gcc-patches
mailing list