Topic: Class Scope Rules


Author: jjb@watson.ibm.com (John Barton)
Date: 1995/05/27
Raw View
In article <3pqg6l$jo4@engnews2.Eng.Sun.COM>, clamage@Eng.Sun.COM (Steve Clamage) writes:
|> In article r67@offas_dike.sbil.co.uk, shepherd@debussy.sbi.com (Marc Shepherd) writes:
|> >In article 508@engnews2.Eng.Sun.COM, clamage@Eng.Sun.COM (Steve Clamage) writes:
|> >>
|> >>
|> >>2. Rule (2) requires that the compiler re-evaluate name bindings at the end
|> >>of the class scope and see whether they have changed. (This requirement
|> >>has linear cost.) When that evaluation is performed on the example code,
|> >>the meaning of 'i' is seen to change, and it is therefore an error which
|> >>must be diagnosed.
|> >
|> >
|> >Steve seems to be saying that there are two kinds of violations
|> >of the re-ordering rule--those that can be detected in linear
|> >time, and that an implementation is required to diagnose; and
|> >those that can be detected only in factorial time, and which an
|> >implementation *is not* required to diagnose.  I find no such
|> >distinction in the text.
|>
|> I think you are confounding several separate issues. My discussion of
|> linear versus factorial time complexity was intended only to illustrate
|> the reasons behind two separate rules: the reconsideration rule, and the
|> reordering rule.
|>
|> The example you originally posted violates the reconsideration rule:
|> A name used in a class winds up with a different binding at its first
|> point of use than it has at the end of the class. That is an error
|> in itself. It simply doesn't matter whether the example also violates the
|> reordering rule (it does, of course), or any other rule.
|>
|> It is possible to violate the reordering rule without violating the
|> reconsideration rule. In that case, the results are undefined. The
|> compiler may detect or ignore the violation, for example.

   Is the last example in 9.3, Scope rules for classes, intended to
illustrate a violation of the reordering without violating the
reconsideration rule?  It is:
   struct Z {
       int f(const R); // Error, first a parameter name, later a type.
       typedef int R;
   };
If so, can we characterize the difference
between these rules as parse-time versus semantics?  When R is
parsed, no R is found, the elided-int type is inserted and
R becomes a parameter.  Later R is added to the scope and the
parse of f() is now incorrect.  Is this always true for the
reordering rule?  Is elided-int type insertion the only cause
for reordering rule failures?

I am fishing for a non-factorial way of warning about "undefined
behavior", ie "mark all elided-int type insertions".

--
John.

John J. Barton        jjb@watson.ibm.com            (914)784-6645
 <http://www.research.ibm.com/xw-SoftwareTechnology>
H1-C13 IBM Watson Research Center P.O. Box 704 Hawthorne NY 10598





Author: shepherd@debussy.sbi.com (Marc Shepherd)
Date: 1995/05/22
Raw View
In article 508@engnews2.Eng.Sun.COM, clamage@Eng.Sun.COM (Steve Clamage) writes:
>In article ihm@offas_dike.sbil.co.uk, shepherd@debussy.sbi.com (Marc Shepherd) writes:
>>Subclause 9.3 says:
[...]

   3) If reordering member declarations in a class yields
      an alternate valid program under (1) and (2), the
      program's behavior is undefined.....

It then goes on to give the following example:

   typedef int c;
   enum { i = 1 };

   class X {
      char v[i];     // error: 'i' refers to ::i
                     // but when reevaluated is X::i
      int f() { return sizeof(c); } // okay: X::c
      char c;
      enum { i = 2 };
   };
[...]
>>The word "error" in the (non-normative) example suggests
>>that the program is unacceptable.  The word "undefined"
>>(in the normative text) suggests that an implementation
>>is entitled to provide any behavior it wants--including
>>compiling the program without complaint.
>
>No, the text is correct.
>
>1. In order for the compiler to detect a violation of the reordering rule,
>it would have to try all N! orderings of the N class members; save
>information about the orderings which did not result in errors; and
>then determine whether the surviving versions were equivalent. (Dependency
>analysis could reduce the number of orderings that actually had to be
>evaluated in some cases.) Language rules that require factorial time to
>implement in the worst case are not acceptable, I hope you will agree.

Certainly.

>
>2. Rule (2) requires that the compiler re-evaluate name bindings at the end
>of the class scope and see whether they have changed. (This requirement
>has linear cost.) When that evaluation is performed on the example code,
>the meaning of 'i' is seen to change, and it is therefore an error which
>must be diagnosed.


Steve seems to be saying that there are two kinds of violations
of the re-ordering rule--those that can be detected in linear
time, and that an implementation is required to diagnose; and
those that can be detected only in factorial time, and which an
implementation *is not* required to diagnose.  I find no such
distinction in the text.

Therefore, unless I have missed the place where such a distinction
is made, it seems to me the comment in the example should say: "undefined
behavior," not "error."



---
Marc Shepherd
Salomon Brothers Inc
shepherd@schubert.sbi.com The opinions I express are no one's but mine!






Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/05/22
Raw View
In article r67@offas_dike.sbil.co.uk, shepherd@debussy.sbi.com (Marc Shepherd) writes:
>In article 508@engnews2.Eng.Sun.COM, clamage@Eng.Sun.COM (Steve Clamage) writes:
>>
>>
>>2. Rule (2) requires that the compiler re-evaluate name bindings at the end
>>of the class scope and see whether they have changed. (This requirement
>>has linear cost.) When that evaluation is performed on the example code,
>>the meaning of 'i' is seen to change, and it is therefore an error which
>>must be diagnosed.
>
>
>Steve seems to be saying that there are two kinds of violations
>of the re-ordering rule--those that can be detected in linear
>time, and that an implementation is required to diagnose; and
>those that can be detected only in factorial time, and which an
>implementation *is not* required to diagnose.  I find no such
>distinction in the text.

I think you are confounding several separate issues. My discussion of
linear versus factorial time complexity was intended only to illustrate
the reasons behind two separate rules: the reconsideration rule, and the
reordering rule.

The example you originally posted violates the reconsideration rule:
A name used in a class winds up with a different binding at its first
point of use than it has at the end of the class. That is an error
in itself. It simply doesn't matter whether the example also violates the
reordering rule (it does, of course), or any other rule.

It is possible to violate the reordering rule without violating the
reconsideration rule. In that case, the results are undefined. The
compiler may detect or ignore the violation, for example.

If some C++ code violates a rule for which violation is an error, and
also violates a rule for which violation leads to undefined behavior,
the code still has the error.

Example:
 const int i = 0;
 int f();
 const_cast<int&>i = f;
The assignment attempts to modify a const object. The results of that are
undefined. The assignment also attempts to assign a function pointer
to an int, a type error which must be diagnosed. Even though the code
contains a rule violation with undefined behavior, it still contains an
error which the compiler must diagnose.

---
Steve Clamage, stephen.clamage@eng.sun.com







Author: shepherd@debussy.sbi.com (Marc Shepherd)
Date: 1995/05/19
Raw View
Subclause 9.3 of the draft WP gives the following scope
rules for classes:

   1) The scope of a name declared in a class consists not only
      of the declarative region following the name's declarator,
      but also of all function bodies, default arguments, and
      constructor initializers in that class (including such
      things in nested classes).

   2) A name N used in a class S shall refer to the same
      declaration when re-evaluated in its context and in
      completed scope of S.

   3) If reordering member declarations in a class yields
      an alternate valid program under (1) and (2), the
      program's behavior is undefined.

         ...

It then goes on to give the following example:

   typedef int c;
   enum { i = 1 };

   class X {
      char v[i];     // error: 'i' refers to ::i
                     // but when reevaluated is X::i
      int f() { return sizeof(c); } // okay: X::c
      char c;
      enum { i = 2 };
   };

To be entirely accurate, it seems to me that either:

   -- a violation of the "reordering rule" (described
      in (3) above) should make the program ill-formed,
      *not* result in undefined behavior.

             OR

   -- The annotation of the example should flag the
      declaration of X::v as "undefined," not an "error".

The word "error" in the (non-normative) example suggests
that the program is unacceptable.  The word "undefined"
(in the normative text) suggests that an implementation
is entitled to provide any behavior it wants--including
compiling the program without complaint.

Am I missing something?


---
Marc Shepherd
Salomon Brothers Inc
shepherd@schubert.sbi.com The opinions I express are no one's but mine!






Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/05/19
Raw View
In article ihm@offas_dike.sbil.co.uk, shepherd@debussy.sbi.com (Marc Shepherd) writes:
>Subclause 9.3 of the draft WP gives the following scope
>rules for classes:
>
>   1) The scope of a name declared in a class consists not only
>      of the declarative region following the name's declarator,
>      but also of all function bodies, default arguments, and
>      constructor initializers in that class (including such
>      things in nested classes).
>
>   2) A name N used in a class S shall refer to the same
>      declaration when re-evaluated in its context and in
>      completed scope of S.
>
>   3) If reordering member declarations in a class yields
>      an alternate valid program under (1) and (2), the
>      program's behavior is undefined.
>
>         ...
>
>It then goes on to give the following example:
>
>   typedef int c;
>   enum { i = 1 };
>
>   class X {
>      char v[i];     // error: 'i' refers to ::i
>                     // but when reevaluated is X::i
>      int f() { return sizeof(c); } // okay: X::c
>      char c;
>      enum { i = 2 };
>   };
>
>To be entirely accurate, it seems to me that either:
>
>   -- a violation of the "reordering rule" (described
>      in (3) above) should make the program ill-formed,
>      *not* result in undefined behavior.
>
>             OR
>
>   -- The annotation of the example should flag the
>      declaration of X::v as "undefined," not an "error".
>
>The word "error" in the (non-normative) example suggests
>that the program is unacceptable.  The word "undefined"
>(in the normative text) suggests that an implementation
>is entitled to provide any behavior it wants--including
>compiling the program without complaint.

No, the text is correct.

1. In order for the compiler to detect a violation of the reordering rule,
it would have to try all N! orderings of the N class members; save
information about the orderings which did not result in errors; and
then determine whether the surviving versions were equivalent. (Dependency
analysis could reduce the number of orderings that actually had to be
evaluated in some cases.) Language rules that require factorial time to
implement in the worst case are not acceptable, I hope you will agree.

2. Rule (2) requires that the compiler re-evaluate name bindings at the end
of the class scope and see whether they have changed. (This requirement
has linear cost.) When that evaluation is performed on the example code,
the meaning of 'i' is seen to change, and it is therefore an error which
must be diagnosed.

---
Steve Clamage, stephen.clamage@eng.sun.com