Topic: Local defs shadowing others


Author: maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller)
Date: Tue, 7 Jul 1992 04:52:09 GMT
Raw View
In article <1992Jul6.162548.9688@rice.edu> amitp@owlnet.rice.edu writes:
>
>|>
>|> I'm all in favor of warnings.  Yes, some do believe that having a
>|> local definition shadow a definition from an enclosing scope should be
>|> illegal.  But it isn't and never will be, since it could break
>|> existing programs.
>
>Why is that bad?  Weren't programs broken when form() was taken out of
>streams?

 Streams are not part of the language. I for one, do not
use them. So none of my programs were broken.

>Weren't programs broken when assignment to this was declared
>a Bad Thing?

 Yes, though not immediately, since compilers usually
had a 'grace' period. But that was an extension and kludge
anyhow.

 Shadowing in nested scopes is a fundamental construct
of block structured languages. Taking it out would break
LOTS of programs.
>
>Sure, you'd have to change
>
> for(int i=1; i<10; i++)
>  gooie(i);
>
> for(i=0; i<50; i++)
>  kablooie(i);
>
>to insert an extra 'int', but it doesn't sound that bad, considering
>the things that have been changed before, and the headaches that would
>be avoided in the future.  (It's a lot less painful than removing all
>references to 'form'!)
>

 For just the case of 'for' loops, I would not disagree.
We could even allow:

 for(const int i=1; i<10; ++i) { ... }

where the ++i is allowed, but i can't be changed in the body.

>
>Just my 0.01c worth,
>
>Amit
>
>--
>Amit J Patel, amitp@cs.rice.edu
>posts += opinions("amitp@cs.rice.edu");
>--
>This .signature VIRUS is a self-referential statement that is true - but
>you will only be able to consistently believe it if you copy it to your own
>signature file!       -copied from fjh@mundil.cs.mu.oz.au


--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: daveg@synaptics.com (Dave Gillespie)
Date: 7 Jul 92 04:43:09 GMT
Raw View
In article <HBF.92Jul5113935@durin.uio.no> hbf@durin.uio.no (Hallvard B Furuseth) writes:
> In article <DOUGM.92Jul4235840@titan.cs.rice.edu> dougm@titan.cs.rice.edu (Doug Moore) writes:
>> A proposal:
>>
>> Let the declaration of a name hide a previous declaration of the same
>> name in its block.
>> (...)
>> there is precedent for a name referring to two different objects
>> within the same block.  So, why not permit this?
>>
>> {
>>   int i;
>>   for (int i = 0; i < n; ++i)
>>     {}
>> }
>
> A multiply declared local variable is usually a bug in the program,
> and we should get error messages for that.  And it's too late to
> redefine 'for (int i= ...) {}' to make 'i' local to the for-statement.

Yes, at least without some pretty horrible kludgery in the language.

> But a compromise should be possible:
>
> Allow a 'for' statmement to redeclare a variable local to its block.
> Any reference to the variable below that 'for' statement will be
> illegal (in the same block), except other declarations in 'for'
> statements.

This would break existing programs.  In fact, this is *why* it's too
late to fix the problem the right way:  If no existing programs
referred to "for" loop variables after the exit of the loop, then
you could change C++ to make the loop variable local with no problem.
Unfortunately, idioms like the following are all too common:

 for (int i = 1; i <= n; i++)
   if (happy(i)) break;
 if (i <= n) use_happy_index(i);

However, there is an (ahem) horrible kludge that you could do to fix
it up.  Simply specify that a variable may be redeclared silently
*if*: a) both declarations appeared in the first clause of a "for"
loop, b) both declarations specify the same type, storage class, etc.,
and c) the second declaration does not appear in the body of the loop
that made the first declaration.  These conditions would be satisfied
by the vast majority of useful cases:

 for (int i = 0; i < n; i++) { ... }
 for (int i = 0; i < m; i++) { ... }

while still disallowing most things that would really be programmer
errors.  Note that in the above example, there is only one variable
"i"; the second loop effectively parses the declaration-with-initializer
"int i = 1" as the assignment "i = 1" instead.  To avoid complications
with constructors, you could even require that the loop variable be of
a simple non-class type in order to be safely redeclared.  I think most
loop variables are ints, and most of the rest are pointers (in "for"
loops along linked lists), both of which have nice, simple semantics
that would easily allow an initializer to be transformed to an
assignment.

Told ya it was kludgey, but it would work, and it sure would make
*my* life easier!

        -- Dave
--
Dave Gillespie
  daveg@synaptics.com, uunet!synaptx!daveg
  or: daveg@csvax.cs.caltech.edu




Author: dougm@titan.cs.rice.edu (Doug Moore)
Date: Sun, 5 Jul 1992 05:58:40 GMT
Raw View
How many times has this happened to you?

You enter a perfectly good function

main()
{
  // PLACEHOLDING COMMENT

  for (int i = 0; i < 10; ++i)
    cout << "!";
}

and later, you replace the comment with

  for (int i = 0; i < 10; ++i)
    cout << "?";

You have just generated a compilation error, because you have two
definitions of the variable i.  You have to make a change to your
program, possibly far from the code you originally intended to modify.

A proposal:

Let the declaration of a name hide a previous declaration of the same
name in its block.  Here is something that's already legal:

{
  int i;
  if (expr)
    {
      int j = i;
      for (int i = 0; i < n; ++i)
 {}
    }
}

Thus, there is precedent for a name referring to two different objects
within the same block.  So, why not permit this?

{
  int i;
  for (int i = 0; i < n; ++i)
    {}
}

The second declaration of i in the block hides the one previously in
effect.

Effect on current programs:
No effect, except that some incorrect programs may begin to work

Implementation cost:
Minimal.

Will it lead to hard-to-detect bugs?  Possibly, but then so can the
sort of name shadowing already permitted.  More often, it will lead to
more programmer convenience.

Comments?

Doug Moore
(dougm@cs.rice.edu)




Author: maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller)
Date: Sun, 5 Jul 1992 11:36:39 GMT
Raw View
In article <DOUGM.92Jul4235840@titan.cs.rice.edu> dougm@titan.cs.rice.edu (Doug Moore) writes:
>How many times has this happened to you?
>
>You enter a perfectly good function
>
>main()
>{
>  // PLACEHOLDING COMMENT
>
>  for (int i = 0; i < 10; ++i)
>    cout << "!";
>}
>
>and later, you replace the comment with
>
>  for (int i = 0; i < 10; ++i)
>    cout << "?";
>
>You have just generated a compilation error, because you have two
>definitions of the variable i.  You have to make a change to your
>program, possibly far from the code you originally intended to modify.
>
 IF you did this, I would insist on a compiler generated
warning! In fact, there ought to be one EVEN if the hiding is of
a variable in an outer scope! Note that some believe THIS should be
illegal (hiding names in the outer scope).

The "for(int i=" in C++ is broken. Bjarne admits this in the ARM,
it is an unfortunate consequence of the first parameter in
a for statement being a statement and not an expression.
You should write

 {for(int i=1; i<10; ++i){ code(); }}

I have macros that do this automatically

 FOR int i=0; i<10; ++i ITERATE code() ENDFOR

so I only have this problem whan I try to conform to the broken
C++ syntax.
>{
>  int i;
>  if (expr)
>    {
>      int j = i;
>      for (int i = 0; i < n; ++i)
> {}
>    }
>}
>
>Thus, there is precedent for a name referring to two different objects
>within the same block.  So, why not permit this?

 Yes, but not DECLARING them twice.

>Effect on current programs:
>No effect, except that some incorrect programs may begin to work
>
>Implementation cost: >Minimal.
>
>Will it lead to hard-to-detect bugs?

 Yes.

>Possibly, but then so can the
>sort of name shadowing already permitted.

 Yes, but why make it worse?

>More often, it will lead to
>more programmer convenience.

 Not much: just put in {} where needed.

A related confusion of mine:

 class X { int i; void f(int i) {int j=i; // ??
 };


--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: hbf@durin.uio.no (Hallvard B Furuseth)
Date: Sun, 5 Jul 1992 10:39:35 GMT
Raw View
In article <DOUGM.92Jul4235840@titan.cs.rice.edu> dougm@titan.cs.rice.edu (Doug Moore) writes:

> A proposal:
>
> Let the declaration of a name hide a previous declaration of the same
> name in its block.
> (...)
> there is precedent for a name referring to two different objects
> within the same block.  So, why not permit this?
>
> {
>   int i;
>   for (int i = 0; i < n; ++i)
>     {}
> }

A multiply declared local variable is usually a bug in the program,
and we should get error messages for that.  And it's too late to
redefine 'for (int i= ...) {}' to make 'i' local to the for-statement.

But a compromise should be possible:

Allow a 'for' statmement to redeclare a variable local to its block.
Any reference to the variable below that 'for' statement will be
illegal (in the same block), except other declarations in 'for'
statements.

Then we'd get

{
 int i;
 use(i);    // OK
 for (int i = 3; ...) {...} // OK
 use(i);    // error
 for (int i = 4; ...) {...} // OK
}
--

Hallvard B Furuseth   email: H.B.Furuseth@usit.uio.no
UNINETT Katalogtjenesten  (Directory-adm@uninett.no)




Author: dougm@titan.cs.rice.edu (Doug Moore)
Date: 6 Jul 92 03:42:03 GMT
Raw View
>>>>> On Sun, 5 Jul 1992 11:36:39 GMT, maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller) said:
 MAX>  IF you did this, I would insist on a compiler generated
 MAX> warning! In fact, there ought to be one EVEN if the hiding is of
 MAX> a variable in an outer scope! Note that some believe THIS should be
 MAX> illegal (hiding names in the outer scope).

I'm all in favor of warnings.  Yes, some do believe that having a
local definition shadow a definition from an enclosing scope should be
illegal.  But it isn't and never will be, since it could break
existing programs.

 MAX> The "for(int i=" in C++ is broken. Bjarne admits this in the ARM,
 MAX> it is an unfortunate consequence of the first parameter in
 MAX> a for statement being a statement and not an expression.
 MAX> You should write

 MAX>  {for(int i=1; i<10; ++i){ code(); }}

 MAX> I have macros that do this automatically

 MAX>  FOR int i=0; i<10; ++i ITERATE code() ENDFOR

 MAX> so I only have this problem whan I try to conform to the broken
 MAX> C++ syntax.

So you agree that the way definitions work is broken.  Perhaps you
wish, as do I, that the scope of a variable declared in a for loop
header was confined to the body of the for loop.  But it's too late
for that now.  Changing it would break existing programs.

What's the second best option?  With no disrespect intended,
preprocessor hackery can't be the right answer.

 DOUG>{
 DOUG>  int i;
 DOUG>  if (expr)
 DOUG>    {
 DOUG>      int j = i;
 DOUG>      for (int i = 0; i < n; ++i)
 DOUG> {}
 DOUG>    }
 DOUG>}
 DOUG>
 DOUG>Thus, there is precedent for a name referring to two different objects
 DOUG>within the same block.  So, why not permit this?

 MAX>  Yes, but not DECLARING them twice.

Clearly, there is no exact precedent for declaring them twice in a
block.  Else I wouldn't have to propose it.  There is precedent for
declaring objects several times at toplevel.  As long as the types of
the declarations are consistent, there is no error.

 DOUG>Will it lead to hard-to-detect bugs?

 MAX>  Yes.

 DOUG>Possibly, but then so can the
 DOUG>sort of name shadowing already permitted.

 MAX>  Yes, but why make it worse?

How much worse?  If the redeclaration of a name in a block had to be
of the same type as the previous declaration within that block, so
that

{
  int i;
  int i;
}

was legal and

{
  int i;
  char i;
}

was not (a rule not too different from what happens at toplevel) and
if the second declaration was *not* a redefinition (and thus referred
to the same object as did the previous declaration), then the problems
would be few.

 DOUG>More often, it will lead to
 DOUG>more programmer convenience.

 MAX>  Not much: just put in {} where needed.

Well, if you think that the shadowing problem is a serious one, and
that putting extra braces in your code is the solution, then you must
not agree with the decision made to permit declarations at other than
the beginnings of blocks.  However, Bjarne Stroustrup* evidently felt
that putting a new set of braces into the code everytime a programmer
wants to declare a new variable is not acceptible.

Doug Moore
(dougm@cs.rice.edu)

*By invoking the name of Bjarne Stroustrup, in no way do I imply that
he has, or will ever, approve or disapprove of this proposal.  He
probably hasn't even read it.  Ya use somebody's name, ya get people
angry at ya if ya aren't careful.




Author: maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller)
Date: Mon, 6 Jul 1992 06:47:49 GMT
Raw View
In article <DOUGM.92Jul5214203@titan.cs.rice.edu> dougm@titan.cs.rice.edu (Doug Moore) writes:
> MAX>  FOR int i=0; i<10; ++i ITERATE code() ENDFOR
>
> MAX> so I only have this problem whan I try to conform to the broken
> MAX> C++ syntax.
>
>So you agree that the way definitions work is broken.  Perhaps you
>wish, as do I, that the scope of a variable declared in a for loop
>header was confined to the body of the for loop.

 Yes, and made read-only too.

>But it's too late
>for that now.  Changing it would break existing programs.
>
>What's the second best option?  With no disrespect intended,
>preprocessor hackery can't be the right answer.

 Agreed. Given the existing language I use preprocessor
defs for IF THEN ELSE ELSIF ENDIF too. But many people
don't want to see this. I find it effective, but agree
it might make it hard to read if you were not used to it.

>
> DOUG>More often, it will lead to
> DOUG>more programmer convenience.
>
> MAX>  Not much: just put in {} where needed.
>
>Well, if you think that the shadowing problem is a serious one, and
>that putting extra braces in your code is the solution, then you must
>not agree with the decision made to permit declarations at other than
>the beginnings of blocks.  However, Bjarne Stroustrup* evidently felt
>that putting a new set of braces into the code everytime a programmer
>wants to declare a new variable is not acceptible.

 No, I liked this feature a lot.

 The correct solution it to abandon the C syntax, it is
really bad. But we won't get that.

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: barmar@think.com (Barry Margolin)
Date: 6 Jul 1992 15:07:34 GMT
Raw View
In article <1992Jul5.113639.10545@ucc.su.OZ.AU> maxtal@extro.ucc.su.OZ.AU (John (MAX) Skaller) writes:
> IF you did this, I would insist on a compiler generated
>warning! In fact, there ought to be one EVEN if the hiding is of
>a variable in an outer scope! Note that some believe THIS should be
>illegal (hiding names in the outer scope).

Making it illegal to shadow variables in an inner scope is a violation of
referential transparency, which is an important feature of block-structured
languages.  A block that only uses the variables that are declared local to
that block should be an independent entity, which should not be affected by
the variable names in the outer scope.

It doesn't seem like a big deal when you look at it from the perspective of
the inner scope shadowing the outer scope's variables.  That's because an
inner scope only has a small number of ancestors, so it's easy to look for
all the variable declarations to find out whether a variable is in use.
But consider the case of adding a variable to an outer scope.  Then you
have to find all its descendents (which could be quite many if you've been
careful to create new blocks whenever you need short-lived temporaries) and
see whether they might shadow the variable you're creating.  Yes, the
compiler can do this easily and generate the appropriate warning, but
what's the point?  If the block that shadows the variable is
self-contained, there's no real conflict.
--
Barry Margolin
System Manager, Thinking Machines Corp.

barmar@think.com          {uunet,harvard}!think!barmar




Author: amitp@newsrice.edu (Amit J. Patel)
Date: 6 Jul 1992 16:25:48 GMT
Raw View
In article <blah blah>, dougm@titan.cs.rice.edu (Doug Moore) writes:
|>
|> I'm all in favor of warnings.  Yes, some do believe that having a
|> local definition shadow a definition from an enclosing scope should be
|> illegal.  But it isn't and never will be, since it could break
|> existing programs.

Why is that bad?  Weren't programs broken when form() was taken out of
streams?  Weren't programs broken when assignment to this was declared
a Bad Thing?

Sure, you'd have to change

 for(int i=1; i<10; i++)
  gooie(i);

 for(i=0; i<50; i++)
  kablooie(i);

to insert an extra 'int', but it doesn't sound that bad, considering
the things that have been changed before, and the headaches that would
be avoided in the future.  (It's a lot less painful than removing all
references to 'form'!)


Just my 0.01c worth,

Amit

--
Amit J Patel, amitp@cs.rice.edu
posts += opinions("amitp@cs.rice.edu");
--
This .signature VIRUS is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
signature file!       -copied from fjh@mundil.cs.mu.oz.au