Date: Tue, 9 Nov 2021 23:49:59 +0800
On Tue, 9 Nov 2021 at 23:04, <language.lawyer_at_[hidden]> wrote:
> On 09/11/2021 17:15, Yongwei Wu wrote:
> > On Tue, 9 Nov 2021 at 00:11, <language.lawyer_at_[hidden]> wrote:
> >
> >> On 08/11/2021 15:32, Yongwei Wu wrote:
> >>> On Mon, 8 Nov 2021 at 17:39, <language.lawyer_at_[hidden]> wrote:
> >>>
> >>>>> Maybe the intention is the same, but it is simply not clear from the
> >> text
> >>>>> that base-class access is defined and unrelated-class access is
> >>>> undefined.
> >>>>> It is clearer in C++17 [basic.lval]/6.10.
> >>>>
> >>>> [basic.lval] is not related to the incoherent object expression issue.
> >>>> And there is nothing special in base vs. non-base class from
> [expr.ref]
> >>>> POV.
> >>>>
> >>>> struct B1 { int i1; };
> >>>> struct B2 { int i2; };
> >>>>
> >>>> struct D : B1, B2 {};
> >>>>
> >>>> D d;
> >>>> B2* b2 = reinterpret_cast<B2*>(&d);
> >>>>
> >>>> After all, you don't read C++17 [basic.lval] as meaning that doing
> >> b2->i2
> >>>> is OK and (should) have defined behavior because it is «base-class
> >> access»?
> >>>>
> >>>
> >>> You do have a point, but this is not how I interpret the C++17 rule.
> >>>
> >>> According to the C++17 rule, if we have:
> >>>
> >>> struct B1 { int i1; };
> >>> struct B2 { int i2; };
> >>> struct B3 { int i3; };
> >>>
> >>> struct D : B1, B2 {};
> >>>
> >>> D* pD = …;
> >>> B2* pB2 = …;
> >>> B3* pB3 = …;
> >>>
> >>> Assuming the compiler cannot determine whether pD and pB2 are related
> >> (say,
> >>> they are passed in to a function), then changes to *pB2 should cause
> the
> >>> compiler to consider *pD is possibly changed. But whatever change made
> to
> >>> *pB3 will not cause the compiler to consider *pD might be tampered
> with.
> >>>
> >>> Does it still hold for C++20? If so, through which rules?
> >>
> >> I don't understand what is your concern here.
> >> Is it again a question about why, if pB3 is used with the -> operator,
> >> then it can be assumed to point to an object of type «cv B3»?
> >>
> >
> > My understanding of the C++17 rule:
> >
> > Writing to *pB2 may change *pD, and the compiler will always assume *pD
> > might have changed after I write to *pB2.
> >
> > Whereas:
> >
> > Writing to *pB3 must not change *pD (which would be undefined behaviour),
> > and the compiler will always assume *pD does not change after I write to
> > *pB3.
> >
> > My question has two parts:
> >
> > 1) Does it still hold for C++20? (I would guess yes.)
> > 2) If so, through which rules? (I cannot deduce the result from the C++20
> > standard text.)
>
> In a well-behaving program, there is no such thing as access
> (read/modification) of an object of class type.
> What can happen — is access to an object of a scalar type through
> different "paths", like pD->i1 and pB2->i1, if pB2 points to a base class
> subobject of *pD.
>
> Knowing that pB2 may point to a base class subobject of *pD is enough for
> a compiler to invalidate its knowledge about pD->i1 when pB2->i1 is
> modified, since these expressions can denote the same (scalar) object.
> I don't understand what kind of rule you are looking for. You have doubts
> that it is allowed to have a pointer to an object and a pointer to its base
> class subobject at the same time? If pD and pB2 are function parameters,
> you may look to the section about function call expression and everything
> which is related to it, like the section about initialization. They define
> behavior without such precondition.
>
It's just aliasing. Function parameter is just one case.
To clarify a bit, the precondition of my discussion is that the scalar
types at specified addresses are the same. So my example uses all ints.
> For the pB3 case, I think I've already wrote that there is not very
> clearly specified rule about object of which type E1 expression in E1.E2
> can denote.
>
It is the main concern. With C++17 or C, it seems modifying *pD by *pB3 is
undefined behaviour. With the current wording/clarification about "access",
I am no longer sure...
To give some background, I was bitten before by GCC for access patterns
like this. So apparently GCC considered it undefined behaviour.
Do you know for sure whether modifying *pD by *pB3 is undefined behaviour
or not NOW?
> On 09/11/2021 17:15, Yongwei Wu wrote:
> > On Tue, 9 Nov 2021 at 00:11, <language.lawyer_at_[hidden]> wrote:
> >
> >> On 08/11/2021 15:32, Yongwei Wu wrote:
> >>> On Mon, 8 Nov 2021 at 17:39, <language.lawyer_at_[hidden]> wrote:
> >>>
> >>>>> Maybe the intention is the same, but it is simply not clear from the
> >> text
> >>>>> that base-class access is defined and unrelated-class access is
> >>>> undefined.
> >>>>> It is clearer in C++17 [basic.lval]/6.10.
> >>>>
> >>>> [basic.lval] is not related to the incoherent object expression issue.
> >>>> And there is nothing special in base vs. non-base class from
> [expr.ref]
> >>>> POV.
> >>>>
> >>>> struct B1 { int i1; };
> >>>> struct B2 { int i2; };
> >>>>
> >>>> struct D : B1, B2 {};
> >>>>
> >>>> D d;
> >>>> B2* b2 = reinterpret_cast<B2*>(&d);
> >>>>
> >>>> After all, you don't read C++17 [basic.lval] as meaning that doing
> >> b2->i2
> >>>> is OK and (should) have defined behavior because it is «base-class
> >> access»?
> >>>>
> >>>
> >>> You do have a point, but this is not how I interpret the C++17 rule.
> >>>
> >>> According to the C++17 rule, if we have:
> >>>
> >>> struct B1 { int i1; };
> >>> struct B2 { int i2; };
> >>> struct B3 { int i3; };
> >>>
> >>> struct D : B1, B2 {};
> >>>
> >>> D* pD = …;
> >>> B2* pB2 = …;
> >>> B3* pB3 = …;
> >>>
> >>> Assuming the compiler cannot determine whether pD and pB2 are related
> >> (say,
> >>> they are passed in to a function), then changes to *pB2 should cause
> the
> >>> compiler to consider *pD is possibly changed. But whatever change made
> to
> >>> *pB3 will not cause the compiler to consider *pD might be tampered
> with.
> >>>
> >>> Does it still hold for C++20? If so, through which rules?
> >>
> >> I don't understand what is your concern here.
> >> Is it again a question about why, if pB3 is used with the -> operator,
> >> then it can be assumed to point to an object of type «cv B3»?
> >>
> >
> > My understanding of the C++17 rule:
> >
> > Writing to *pB2 may change *pD, and the compiler will always assume *pD
> > might have changed after I write to *pB2.
> >
> > Whereas:
> >
> > Writing to *pB3 must not change *pD (which would be undefined behaviour),
> > and the compiler will always assume *pD does not change after I write to
> > *pB3.
> >
> > My question has two parts:
> >
> > 1) Does it still hold for C++20? (I would guess yes.)
> > 2) If so, through which rules? (I cannot deduce the result from the C++20
> > standard text.)
>
> In a well-behaving program, there is no such thing as access
> (read/modification) of an object of class type.
> What can happen — is access to an object of a scalar type through
> different "paths", like pD->i1 and pB2->i1, if pB2 points to a base class
> subobject of *pD.
>
> Knowing that pB2 may point to a base class subobject of *pD is enough for
> a compiler to invalidate its knowledge about pD->i1 when pB2->i1 is
> modified, since these expressions can denote the same (scalar) object.
> I don't understand what kind of rule you are looking for. You have doubts
> that it is allowed to have a pointer to an object and a pointer to its base
> class subobject at the same time? If pD and pB2 are function parameters,
> you may look to the section about function call expression and everything
> which is related to it, like the section about initialization. They define
> behavior without such precondition.
>
It's just aliasing. Function parameter is just one case.
To clarify a bit, the precondition of my discussion is that the scalar
types at specified addresses are the same. So my example uses all ints.
> For the pB3 case, I think I've already wrote that there is not very
> clearly specified rule about object of which type E1 expression in E1.E2
> can denote.
>
It is the main concern. With C++17 or C, it seems modifying *pD by *pB3 is
undefined behaviour. With the current wording/clarification about "access",
I am no longer sure...
To give some background, I was bitten before by GCC for access patterns
like this. So apparently GCC considered it undefined behaviour.
Do you know for sure whether modifying *pD by *pB3 is undefined behaviour
or not NOW?
Received on 2021-11-09 09:49:57