Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #142991 > unrolled thread

Scopes of identifiers

Started byThiago Adams <thiago.adams@gmail.com>
First post2019-04-10 16:15 -0700
Last post2019-04-11 23:26 -0400
Articles 20 on this page of 41 — 14 participants

Back to article view | Back to comp.lang.c


Contents

  Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-10 16:15 -0700
    Re: Scopes of identifiers Bart <bc@freeuk.com> - 2019-04-11 00:33 +0100
      Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-10 16:45 -0700
      Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-10 18:31 -0700
    Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-10 16:34 -0700
      Re: Scopes of identifiers Robert Wessel <robertwessel2@yahoo.com> - 2019-04-10 20:50 -0500
      Re: Scopes of identifiers James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-04-10 20:02 -0700
      Re: Scopes of identifiers Keith Thompson <kst-u@mib.org> - 2019-04-10 20:58 -0700
        Re: Scopes of identifiers luser droog <luser.droog@gmail.com> - 2019-04-10 21:48 -0700
        Re: Scopes of identifiers Andrey Tarasevich <andreytarasevich@hotmail.com> - 2019-04-11 11:57 -0700
          Re: Scopes of identifiers scott@slp53.sl.home (Scott Lurndal) - 2019-04-11 19:12 +0000
            Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-11 12:35 -0700
              Re: Scopes of identifiers Ben Bacarisse <ben.usenet@bsb.me.uk> - 2019-04-12 01:11 +0100
                Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-12 05:29 -0700
                  Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-12 05:39 -0700
          Re: Scopes of identifiers Keith Thompson <kst-u@mib.org> - 2019-04-11 14:00 -0700
            Re: Scopes of identifiers Nick Bowler <nbowler@draconx.ca> - 2019-04-15 19:48 +0000
      Re: Scopes of identifiers Bart <bc@freeuk.com> - 2019-04-11 10:36 +0100
        Re: Scopes of identifiers Bart <bc@freeuk.com> - 2019-04-11 14:25 +0100
          Re: Scopes of identifiers Keith Thompson <kst-u@mib.org> - 2019-04-11 10:20 -0700
      Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-11 05:00 -0700
      Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-11 06:42 -0700
        Re: Scopes of identifiers James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-04-11 08:11 -0700
    Re: Scopes of identifiers BGB <cr88192@gmail.com> - 2019-04-10 22:35 -0500
      Re: Scopes of identifiers Keith Thompson <kst-u@mib.org> - 2019-04-10 22:04 -0700
        Re: Scopes of identifiers BGB <cr88192@gmail.com> - 2019-04-11 05:46 -0500
    Re: Scopes of identifiers John Bode <jfbode1029@gmail.com> - 2019-04-11 12:04 -0700
      Re: Scopes of identifiers James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-04-11 13:18 -0700
      Re: Scopes of identifiers Keith Thompson <kst-u@mib.org> - 2019-04-11 14:31 -0700
    Re: Scopes of identifiers Andrey Tarasevich <andreytarasevich@hotmail.com> - 2019-04-11 12:28 -0700
      Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-11 12:50 -0700
      Re: Scopes of identifiers James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-04-11 14:20 -0700
        Re: Scopes of identifiers Thiago Adams <thiago.adams@gmail.com> - 2019-04-11 14:43 -0700
        Re: Scopes of identifiers Andrey Tarasevich <andreytarasevich@hotmail.com> - 2019-04-11 15:04 -0700
          Re: Scopes of identifiers Keith Thompson <kst-u@mib.org> - 2019-04-11 17:08 -0700
            Re: Scopes of identifiers Andrey Tarasevich <andreytarasevich@hotmail.com> - 2019-04-11 18:45 -0700
              Re: Scopes of identifiers James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-04-11 23:16 -0400
              Re: Scopes of identifiers Keith Thompson <kst-u@mib.org> - 2019-04-11 20:19 -0700
              Re: Scopes of identifiers Tim Rentsch <tr.17687@z991.linuxsc.com> - 2019-04-12 14:39 -0700
              Re: Scopes of identifiers Ike Naar <ike@sdf.lonestar.org> - 2019-04-13 20:02 +0000
        Re: Scopes of identifiers James Kuyper <jameskuyper@alumni.caltech.edu> - 2019-04-11 23:26 -0400

Page 2 of 3 — ← Prev page 1 [2] 3  Next page →


#143004

FromThiago Adams <thiago.adams@gmail.com>
Date2019-04-11 05:00 -0700
Message-ID<c0fcd5d1-9eb0-4da2-b4f0-8f913b820e7a@googlegroups.com>
In reply to#142993
On Wednesday, April 10, 2019 at 8:34:34 PM UTC-3, Thiago Adams wrote:
> On Wednesday, April 10, 2019 at 8:16:14 PM UTC-3, Thiago Adams wrote:
> > How scopes are implemented in C compilers?
> 
> It is also interesting that structs don't introduce scopes.
> 
> struct X
> {
>   struct X2
>   {
>     int i;
>   } x2;
>  
>   int i;
> };
> 
> We can declare structs inside structs,or enum but not 
> functions or typedefs.


The rules here are different from C++.
C++ creates a scope like X::X2 and also allows typedefs.

I usually don't define structs or enum inside structs
because the code will be incompatible with C++ rules.

But I think it makes sense in case both were created 
to work together and never apart from each other.

struct X
{
   enum E {A, B, C} e;
}

[toc] | [prev] | [next] | [standalone]


#143012

FromThiago Adams <thiago.adams@gmail.com>
Date2019-04-11 06:42 -0700
Message-ID<c01a169e-0919-46e3-b7cf-397547330529@googlegroups.com>
In reply to#142993
On Wednesday, April 10, 2019 at 8:34:34 PM UTC-3, Thiago Adams wrote:
> On Wednesday, April 10, 2019 at 8:16:14 PM UTC-3, Thiago Adams wrote:
> > How scopes are implemented in C compilers?
> 
> It is also interesting that structs don't introduce scopes.

One more interesting (or strange) aspect of the language
is that we can declare enuns or struct using it.

For instance:

enum { OutOfMem = -1, NotAdded = 0, Added = 1} Add()
{
    return Added ;
}

[toc] | [prev] | [next] | [standalone]


#143014

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2019-04-11 08:11 -0700
Message-ID<75131655-d086-486b-b586-444d07537f80@googlegroups.com>
In reply to#143012
On Thursday, April 11, 2019 at 9:42:55 AM UTC-4, Thiago Adams wrote:
> On Wednesday, April 10, 2019 at 8:34:34 PM UTC-3, Thiago Adams wrote:
> > On Wednesday, April 10, 2019 at 8:16:14 PM UTC-3, Thiago Adams wrote:
> > > How scopes are implemented in C compilers?
> > 
> > It is also interesting that structs don't introduce scopes.
> 
> One more interesting (or strange) aspect of the language
> is that we can declare enuns or struct using it.
> 
> For instance:
> 
> enum { OutOfMem = -1, NotAdded = 0, Added = 1} Add()
> {
>     return Added ;
> }

Tags for user defined types (structs, unions, and enumerations) can be
declared (and even defined) in any location where a type name is
allowed. That includes function return types, declarations of objects,
including members of a struct or union, and function parameter
declarations. It also includes casts, compound literals, _Generic, and
_Alignas. On rare occasions I've seen a tag declared in the same
declaration as an object of that type or a function returning that type.
However, I've never seen real-world code that made any use of the other
possibilities mentioned above. That is why C++ got away with prohibiting
such things.

Such declarations give the tag a scope that, in some cases, is too small
to be useful. For instance, if you write a function declaration that is
not a function definition, and declare a tag while declaring the type of
one of the function parameters, that tag has function prototype scope.
That scope therefore ends at the end of the function declaration, which
means that the tag cannot be used when declaring something to be passed
to the function as an argument.

[toc] | [prev] | [next] | [standalone]


#142998

FromBGB <cr88192@gmail.com>
Date2019-04-10 22:35 -0500
Message-ID<q8mclh$tr6$1@dont-email.me>
In reply to#142991
On 4/10/2019 6:15 PM, Thiago Adams wrote:
> How scopes are implemented in C compilers?
> 
> Sample:
> 
> struct X
> {
>      int X;
> };
> 
> typedef int X;
> 
> void F(struct X X)
> {
>    X:
>    return;
> }
> 
> I believe they have two stacks of dictionaries (symbol map).
> 

In my case (for the toplevel):
   A collection of all typedefs;
   A collection of all structs;
   A collection of all globals (variables and functions).

Though, in terms of behavior, I suspect GCC may use two separate scopes, 
one for global variables and one for functions. Accessing a variable or 
calling a function will first check one, then the other. It may then in 
certain cases allow variables and functions to exist with the same names 
(whereas both my compiler and MSVC seem to reject this outright).

Label names are transient, and are only really used in the frontend in 
my case. In the backend, these are all mapped to "label numbers" 
(effectively ranges within a 32-bit integer value).

Label numbers are subdivided into multiple ranges:
   Global indices (index gives a global variable or function);
   Frontend GenSyms, used for both explicit and implicit labels;
   Backend GenSyms, those generated as part of the code-generation;
     Subdivided into "stable" and "temporary" ranges.
   Special, used mostly for arch-registers and intrinsics and similar;
   A range for sign-extended 17-bit numbers;
   ...

Note that "labels" and disjoint from "registers", which are typically 
what are used for variable or function references.
The "register" numbers are 64-bit, and encode various cases:
   temp / arg / local / global variables;
   int / long / float / double / string / ... literals;
   ...

Both long and double literals are encoded with 61 bits; if it doesn't 
fit it uses an escape to a table (of 64-bit elements). Int128 and 
Float128 literals are encoded as a pair of indexes into the table of 
literals. Pretty much any variable or literal which can be expressed in 
the language can be represented via this.

Another option would be to use the same representation for both, with 
some "sanity" restrictions.


In the frontend, a nested stack of lexical scopes exists (with each code 
block given its own index number), however this is "flattened out" by 
the time it reaches the bytecode IR. So, each function effectively 
contains a single flat list of local variables, referred to by an index 
number (rather than by name).

A scope-search is performed in the frontend to local where variables are 
located, but the rest of the compiler doesn't generally do it this way 
(and instead uses "register" indices for nearly everything).


Similar applies to structs:
   Each struct contains its own local list of fields;
   Nested anonymous structs are handled as multiple nested structs;
   Struct field access (in the middle and backend) uses index numbers.
     For BS2/C++/...: Also encodes if it is a field/method/...

There are two separate index spaces in place for things like global 
variables and functions; and another space for structs/etc (pretty much 
anything which isn't a global function or variable).

Another compiler of mine (specifically for BS2) uses a single index 
space for both cases.

Each of these then contains a list of any fields/local vars/... (which 
may be interpreted differently depending on whether it is a struct or a 
function, ...).


This can be contrast with .NET, which uses a collection of 
interconnected tables for metadata; rather than a single large array of 
globals/structures/...


> One for
> struct/union/enum
> and the other for
> typedef/function/variables/enum constant
> 
> The file scope starts two stacks with one dictionary each.
> When the function argument starts a new dictionary is pushed in each
> stack. This is also for each { } scope and one for 'for'.
> 
> This is the kind of question I think help us to understand the C
> language so is on topic here.
> 
> But it also can easily go off-topic and can be analysed with more
> details at
> 
> https://groups.google.com/forum/#!forum/c-compilers
> 

...

[toc] | [prev] | [next] | [standalone]


#143001

FromKeith Thompson <kst-u@mib.org>
Date2019-04-10 22:04 -0700
Message-ID<lnftqp2kut.fsf@kst-u.example.com>
In reply to#142998
BGB <cr88192@gmail.com> writes:
[...]
> Though, in terms of behavior, I suspect GCC may use two separate scopes, 
> one for global variables and one for functions. Accessing a variable or 
> calling a function will first check one, then the other. It may then in 
> certain cases allow variables and functions to exist with the same names 
> (whereas both my compiler and MSVC seem to reject this outright).

Names of objects and names of functions follow the same scoping rules,
with inner declarations hiding outer declarations of the same name.  Do
you have an example where gcc accepts a function and a variable with the
same name, declared in the same scope?

[...]

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#143003

FromBGB <cr88192@gmail.com>
Date2019-04-11 05:46 -0500
Message-ID<q8n5tq$im8$1@dont-email.me>
In reply to#143001
On 4/11/2019 12:04 AM, Keith Thompson wrote:
> BGB <cr88192@gmail.com> writes:
> [...]
>> Though, in terms of behavior, I suspect GCC may use two separate scopes,
>> one for global variables and one for functions. Accessing a variable or
>> calling a function will first check one, then the other. It may then in
>> certain cases allow variables and functions to exist with the same names
>> (whereas both my compiler and MSVC seem to reject this outright).
> 
> Names of objects and names of functions follow the same scoping rules,
> with inner declarations hiding outer declarations of the same name.  Do
> you have an example where gcc accepts a function and a variable with the
> same name, declared in the same scope?
> 

I may have been incorrect, testing again (trying to write a trivial 
example) and GCC also seems to reject these cases with "foo redeclared 
as different kind of symbol" errors, but I had thought I had remembering 
encountering something like this in the past.

(I may later try to find the original test-case, and try again and see 
what happens...).

...

[toc] | [prev] | [next] | [standalone]


#143021

FromJohn Bode <jfbode1029@gmail.com>
Date2019-04-11 12:04 -0700
Message-ID<07db382c-2c22-4617-b643-906da26d5cd4@googlegroups.com>
In reply to#142991
On Wednesday, April 10, 2019 at 6:16:14 PM UTC-5, Thiago Adams wrote:
> How scopes are implemented in C compilers?
> 
> Sample:
> 
> struct X
> {
>     int X;
> };
> 
> typedef int X;
> 

This is a matter of _name spaces_, not scope.

> void F(struct X X)
> {
>   X:   
>   return; 
> }
> 
> I believe they have two stacks of dictionaries (symbol map).
> 
> One for 
> struct/union/enum
> and the other for 
> typedef/function/variables/enum constant
> 

There are four _name spaces_:

1.  Labels (disambiguated by a leading 'goto' keyword or trailing ':');
2. Struct, union, and enum _tag names_ (disambiguated by a preceding 'struct', 'union', or 
    'enum' keyword);
3. Struct and union _member names_ (disambiguated by an identifier followed by either 
    a '.' or '->' operator);
4. All other identifiers.

In the case of 

    struct X 
    {
      int X;
    };

the _tag name_ 'X' occupies a different namespace than the _member name_ 'X'.  These
are both different namespaces from ordinary identifiers, so it's possible to write

    struct X X;
    ...
    X.X = some_value();

Typedef names occupy the same namespace as ordinary identifiers, so it is *not* possible
to write:

    typedef struct X X;
    X X;

IOW, you can't use X as both a typedef name and an identifier name.

> The file scope starts two stacks with one dictionary each.
> When the function argument starts a new dictionary is pushed in each
> stack. This is also for each { } scope and one for 'for'.
> 

It's been on the order of 30 years since I implemented a compiler, and that was in 
college for a toy language, so I don't remember too many details.  

IIRC there are a number of strategies for managing identifiers in multiple scopes and 
namespaces, and that gets into implementation details that are beyond the scope of
the language.  IOW, it doesn't matter *how* you manage your symbols as long as the 
result is a valid translation of the source code.

[toc] | [prev] | [next] | [standalone]


#143032

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2019-04-11 13:18 -0700
Message-ID<b846fe15-62cd-4e21-998c-fb3b631439a6@googlegroups.com>
In reply to#143021
On Thursday, April 11, 2019 at 3:04:55 PM UTC-4, John Bode wrote:
...
> There are four _name spaces_:
> 
> 1.  Labels (disambiguated by a leading 'goto' keyword or trailing ':');
> 2. Struct, union, and enum _tag names_ (disambiguated by a preceding 'struct', 'union', or 
>     'enum' keyword);
> 3. Struct and union _member names_ (disambiguated by an identifier followed by either 
>     a '.' or '->' operator);
> 4. All other identifiers.

Not quite. There's four types of names spaces, and for three of those
types, there's only one name space of that type. However, "Each
structure or union has a separate name space for its members" (6.2.3p1),
so there's no upper limit on the number of name spaces.

[toc] | [prev] | [next] | [standalone]


#143038

FromKeith Thompson <kst-u@mib.org>
Date2019-04-11 14:31 -0700
Message-ID<ln36mo2pqd.fsf@kst-u.example.com>
In reply to#143021
John Bode <jfbode1029@gmail.com> writes:
[...]
> There are four _name spaces_:
>
> 1.  Labels (disambiguated by a leading 'goto' keyword or trailing ':');
> 2. Struct, union, and enum _tag names_ (disambiguated by a preceding 'struct', 'union', or 
>     'enum' keyword);
> 3. Struct and union _member names_ (disambiguated by an identifier followed by either 
>     a '.' or '->' operator);
> 4. All other identifiers.

A small correction: a member name can be preceded by an expression
(more precisely a postfix-expression) followed by a "." or "->",
not just an identifier.  For example `function_call().member` or
`array[index]->member` is valid.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#143023

FromAndrey Tarasevich <andreytarasevich@hotmail.com>
Date2019-04-11 12:28 -0700
Message-ID<q8o4h9$dqe$1@dont-email.me>
In reply to#142991
On 4/10/2019 4:15 PM, Thiago Adams wrote:
> How scopes are implemented in C compilers?
> ...

Here's a few interesting scoping rules that I keep in my notes. The 
"interesting" aspect in this case is mostly that the code looks quite 
innocent from the C++ point of view, but in reality it is valid C, but 
not valid C++ (or, sometimes, vice versa).

1. A simple and quite unremarkable one: enum/struct tags and typedef 
names belong to different namespaces and don't conflict with each other

     struct A { int i; };
     typedef struct B { int i; } A;

I.e. `A` and `struct A` are two completely different and unrelated types.

2. An unknown struct tag mentioned in function parameter list is treated 
as a declaration of a new type. That type is _local_ to that function 
(or even local to the prototype!). In case of full function definition, 
it is possible to complete the definition of the type inside the function

     /* Assume `struct S` is unknown at this point */

     void foo(struct S *p)    /* Introduces a local `struct S` type */
     {
       struct S { int a; } s; /* Completes the type */
       p = &s;                /* No pointer type mismatch here */
       p->a = 5;              /* OK */
     }

C++ also allows one to introduce new (incomplete) types in function 
parameter lists. However, in C++ an unknown type `struct S` mentioned in 
parameter list would be treated as belonging to _namespace_ scope. The 
inner `struct S` would not be associated with it at all. The above code 
would be invalid in C++.

3. Since new types introduced in the prototype declarations are local to 
that prototype (have prototype scope), the following innocent-looking 
sequence of declarations is actually invalid in C

     /* Assume `struct S` is unknown at this point */

     void foo(struct S *); /* Introduces prototype-scope `struct S` */

     void foo(struct S *) {}
     /* Introduces another local `struct S`, unrelated to `struct S`
        from the previous declaration. The code is invalid since the
        function `foo` is redeclared with non-matching prototype */

4. Meanwhile, a unknown struct tag mentioned in _return_ type of a 
function is treated as a declaration of a new type that belongs to 
_file_ scope.

The following code is valid in C

     /* Assume `struct S` is unknown at this point */

     struct S *foo() { return 0; }
     /* Introduces file-scope `struct S` */

     /* Type `struct S` is known here */

     void bar(struct S *) {}
     /* Parameter type refers to the previously introduced `struct S` */

     int main(void)
     {
       bar(foo());
       /* Result of `foo()` is correct argument type for `bar()` */
     }

However, if we simply swap the function declarations, the code will 
suddenly become invalid

     /* Assume `struct S` is unknown at this point */

     void bar(struct S *) {}
     /* Parameter type introduces a prototype-scope `struct S *` */

     /* Type `struct S` is still unknown here */

     struct S *foo() { return 0; }
     /* Introduces file-scope `struct S`, unrelated to the `struct S`
        in the previous declaration */

     int main(void)
     {
       bar(foo()); /* Error: pointer type mismatch */
     }

5. In modern C (after C99) many statements introduce their own 
_implicit_ scopes, in addition to the explicit scopes introduced by 
explicit `{}` blocks. These scopes nest and thus allow declarations of 
identical identifiers. For example

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

In this case the inner `i` has its own nested scope and does not 
conflict with `i` from the cycle header. This is different from C++ 
where the whole cycle is one scope and the inner declaration would be 
illegal.

6. Implicit scopes around some statements in C introduced by C99 are a 
*breaking change* since C89/90. Some esoteric C89/90 will fail to 
compile in C99 because of this change.

-- 
Best regards,
Andrey Tarasevich

[toc] | [prev] | [next] | [standalone]


#143026

FromThiago Adams <thiago.adams@gmail.com>
Date2019-04-11 12:50 -0700
Message-ID<1bebbf36-4709-468d-a7af-fa402eeab982@googlegroups.com>
In reply to#143023
On Thursday, April 11, 2019 at 4:28:49 PM UTC-3, Andrey Tarasevich wrote:
> On 4/10/2019 4:15 PM, Thiago Adams wrote:
> > How scopes are implemented in C compilers?
> > ...
> 
> Here's a few interesting scoping rules that I keep in my notes. The 
> "interesting" aspect in this case is mostly that the code looks quite 
> innocent from the C++ point of view, but in reality it is valid C, but 
> not valid C++ (or, sometimes, vice versa).
> 
> 1. A simple and quite unremarkable one: enum/struct tags and typedef 
> names belong to different namespaces and don't conflict with each other
> 
>      struct A { int i; };
>      typedef struct B { int i; } A;
> 
> I.e. `A` and `struct A` are two completely different and unrelated types.
> 
> 2. An unknown struct tag mentioned in function parameter list is treated 
> as a declaration of a new type. That type is _local_ to that function 
> (or even local to the prototype!). In case of full function definition, 
> it is possible to complete the definition of the type inside the function
> 
>      /* Assume `struct S` is unknown at this point */
> 
>      void foo(struct S *p)    /* Introduces a local `struct S` type */
>      {
>        struct S { int a; } s; /* Completes the type */
>        p = &s;                /* No pointer type mismatch here */
>        p->a = 5;              /* OK */
>      }
> 
> C++ also allows one to introduce new (incomplete) types in function 
> parameter lists. However, in C++ an unknown type `struct S` mentioned in 
> parameter list would be treated as belonging to _namespace_ scope. The 
> inner `struct S` would not be associated with it at all. The above code 
> would be invalid in C++.
> 
> 3. Since new types introduced in the prototype declarations are local to 
> that prototype (have prototype scope), the following innocent-looking 
> sequence of declarations is actually invalid in C
> 
>      /* Assume `struct S` is unknown at this point */
> 
>      void foo(struct S *); /* Introduces prototype-scope `struct S` */
> 
>      void foo(struct S *) {}
>      /* Introduces another local `struct S`, unrelated to `struct S`
>         from the previous declaration. The code is invalid since the
>         function `foo` is redeclared with non-matching prototype */
> 
> 4. Meanwhile, a unknown struct tag mentioned in _return_ type of a 
> function is treated as a declaration of a new type that belongs to 
> _file_ scope.
> 
> The following code is valid in C
> 
>      /* Assume `struct S` is unknown at this point */
> 
>      struct S *foo() { return 0; }
>      /* Introduces file-scope `struct S` */
> 
>      /* Type `struct S` is known here */
> 
>      void bar(struct S *) {}
>      /* Parameter type refers to the previously introduced `struct S` */
> 
>      int main(void)
>      {
>        bar(foo());
>        /* Result of `foo()` is correct argument type for `bar()` */
>      }
> 
> However, if we simply swap the function declarations, the code will 
> suddenly become invalid
> 
>      /* Assume `struct S` is unknown at this point */
> 
>      void bar(struct S *) {}
>      /* Parameter type introduces a prototype-scope `struct S *` */
> 
>      /* Type `struct S` is still unknown here */
> 
>      struct S *foo() { return 0; }
>      /* Introduces file-scope `struct S`, unrelated to the `struct S`
>         in the previous declaration */
> 
>      int main(void)
>      {
>        bar(foo()); /* Error: pointer type mismatch */
>      }
> 
> 5. In modern C (after C99) many statements introduce their own 
> _implicit_ scopes, in addition to the explicit scopes introduced by 
> explicit `{}` blocks. These scopes nest and thus allow declarations of 
> identical identifiers. For example
> 
>      for (int i = 0; i < 10; ++i)
>      {
>        int i = 42;
>      }
> 
> In this case the inner `i` has its own nested scope and does not 
> conflict with `i` from the cycle header. This is different from C++ 
> where the whole cycle is one scope and the inner declaration would be 
> illegal.
> 
> 6. Implicit scopes around some statements in C introduced by C99 are a 
> *breaking change* since C89/90. Some esoteric C89/90 will fail to 
> compile in C99 because of this change.
> 

Very interesting points.

[toc] | [prev] | [next] | [standalone]


#143037

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2019-04-11 14:20 -0700
Message-ID<5b59a4e9-81d3-4442-a9e6-6fa8419a08e7@googlegroups.com>
In reply to#143023
On Thursday, April 11, 2019 at 3:28:49 PM UTC-4, Andrey Tarasevich wrote:
...
> 5. In modern C (after C99) many statements introduce their own 
> _implicit_ scopes, in addition to the explicit scopes introduced by 
> explicit `{}` blocks.

The C standard does not use the term "implicit scope". It just uses the
term "block", and identifiers declared within a block have block scope.
Every selection (if, switch) and iteration (while, do-while, for)
statement has one or two sub-statements that it controls execution of,
and those sub-statements have their own blocks (even if they're not
compound statements). None of that changed in C99.

What did change in C99 was that each selection and iteration statement
now has it's own block, containing the entire statement; the block for
the controlled sub-statement is a sub-block of the outer block.

C99 added the ability to declare an object identifier in a for()
statement, inside the first part of the block associated with that
statement, but outside the block associated with the sub-statement.
However, that's not true for the other selection and iteration
statements. Therefore, it might seem pointless to give those other
statements extended blocks as well.

However, you can declare a tag for a user defined type, or the
enumerators for an enumerated type, as part of any expression that
involves a type name. If such identifiers have not already been
declared, and are declared in the outer block of one of those
statements, they will have a scope restricted to that statement. I
believe that in previous versions of C they would have had scope
associated with the block containing that statement.

> ... These scopes nest

As do all other scopes.

> ... and thus allow declarations of 
> identical identifiers. For example
> 
>      for (int i = 0; i < 10; ++i)
>      {
>        int i = 42;
>      }
> 
> In this case the inner `i` has its own nested scope and does not 
> conflict with `i` from the cycle header.

I believe this is incorrect, though I can't test it with a compiler
right now. The scope of the first 'i' ends with the '}' at the end of
the loop; the same is true of the inner 'i'.

"Two identifiers have the _same scope_ if and only if their scopes
terminate at the same point." (6.2.1p6). The phrase "same scope" is
italicized, an ISO convention indicating that this clause is considered
to be the official definition of the italicized phrase.
That phrase is used in 6.7p3: "If an identifier has no linkage, there
shall be no more than one declaration of the identifier (in a declarator
or type specifier) with the same scope and in the same name space, ...".
That's a constraint violated by your code.

[toc] | [prev] | [next] | [standalone]


#143039

FromThiago Adams <thiago.adams@gmail.com>
Date2019-04-11 14:43 -0700
Message-ID<70dd00a7-885a-4242-820d-22d1a962149f@googlegroups.com>
In reply to#143037
On Thursday, April 11, 2019 at 6:21:02 PM UTC-3, James Kuyper wrote:
...
> > ... and thus allow declarations of 
> > identical identifiers. For example
> > 
> >      for (int i = 0; i < 10; ++i)
> >      {
> >        int i = 42;
> >      }
> > 
> > In this case the inner `i` has its own nested scope and does not 
> > conflict with `i` from the cycle header.
> 
> I believe this is incorrect, though I can't test it with a compiler
> right now. The scope of the first 'i' ends with the '}' at the end of
> the loop; the same is true of the inner 'i'.

It compiles.(You can use these online compilers)

I think that:

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

is the same of

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

[toc] | [prev] | [next] | [standalone]


#143042

FromAndrey Tarasevich <andreytarasevich@hotmail.com>
Date2019-04-11 15:04 -0700
Message-ID<q8odkv$60b$1@dont-email.me>
In reply to#143037
On 4/11/2019 2:20 PM, James Kuyper wrote:
> On Thursday, April 11, 2019 at 3:28:49 PM UTC-4, Andrey Tarasevich wrote:
> ...
>> 5. In modern C (after C99) many statements introduce their own
>> _implicit_ scopes, in addition to the explicit scopes introduced by
>> explicit `{}` blocks.
> 
> The C standard does not use the term "implicit scope". It just uses the
> term "block", and identifiers declared within a block have block scope.
> Every selection (if, switch) and iteration (while, do-while, for)
> statement has one or two sub-statements that it controls execution of,
> and those sub-statements have their own blocks (even if they're not
> compound statements). None of that changed in C99.

It is true that the standard uses the term "block" to describe the 
additional blocks introduced in C99 for iteration and selection statements.

However, what you state above is not entirely accurate. There's no 
requirement to create blocks for controlled/associated statements. It is 
entirely up to user's decision: if you use an explicit pair of `{}` 
around the associated statement - you get a block. If you don't use the 
`{}` - you don't get a block.

Consider this example

      int a = 0;
      if ((enum { X, Y, Z }) a == X)
        a = sizeof(enum { A, B, C });
      else
        a = B;

This is perfectly valid C90/90 code. And note that the declaration of 
`B` made in the "true" branch of that `if` is perfectly visible in the 
"false" branch.

What C99 change (as you noted yourself), it created additional 
"implicit" blocks around each selection and iteration statement. But it 
also additionally enclosed each individual branch of `if` statement in 
its own "implicit" block. The above code will no longer compile in C99 
because `B` is no longer visible in the "false" branch.

> What did change in C99 was that each selection and iteration statement
> now has it's own block, containing the entire statement; the block for
> the controlled sub-statement is a sub-block of the outer block.

Correct. Plus, again, since C99 each sub-branch of an `if` is now also a 
separate block.

> C99 added the ability to declare an object identifier in a for()
> statement, inside the first part of the block associated with that
> statement, but outside the block associated with the sub-statement.
> However, that's not true for the other selection and iteration
> statements. Therefore, it might seem pointless to give those other
> statements extended blocks as well.

I remember that we already discussed it before. The primary reason we 
have those "implicit" block around all these statements is _not_ the 
ability to declare iteration variable in the `for` header. The real 
reason is the need to limit the lifetime of *compound literals*. C99 
*compound literals* gave the user ability to create [nameless] local 
objects without using declaration syntax. Something had to be done with 
the lifetime of these objects. Before C99 this issue simply did not 
exist. And in order give these object C++-like lifetime properties the 
language has to resort to C++-like scoping/lifetime rules for all these 
statements. Hence these new "implicit" blocks in C99.

> However, you can declare a tag for a user defined type, or the
> enumerators for an enumerated type, as part of any expression that
> involves a type name. If such identifiers have not already been
> declared, and are declared in the outer block of one of those
> statements, they will have a scope restricted to that statement. I
> believe that in previous versions of C they would have had scope
> associated with the block containing that statement.

True. But I'm not sure what this is doing here and what point it is 
trying to address.

> 
>> ... and thus allow declarations of
>> identical identifiers. For example
>>
>>       for (int i = 0; i < 10; ++i)
>>       {
>>         int i = 42;
>>       }
>>
>> In this case the inner `i` has its own nested scope and does not
>> conflict with `i` from the cycle header.
> 
> I believe this is incorrect, though I can't test it with a compiler
> right now. The scope of the first 'i' ends with the '}' at the end of
> the loop; the same is true of the inner 'i'.

This code is perfectly correct and actually fully agrees with what you 
_yourself_ were stating above.

> "Two identifiers have the _same scope_ if and only if their scopes
> terminate at the same point." (6.2.1p6). The phrase "same scope" is
> italicized, an ISO convention indicating that this clause is considered
> to be the official definition of the italicized phrase.

No. Nested blocks mean nested scopes in C. It does not matter whether 
the block is "explicit" (a pair of `{}`) or "implicit" (as described 
above). This immediately means that scopes associated with nested blocks 
have different termination points.

There's no difference between the above example and simple

     {
       int i = 42;
       {
         int i = 5;
       }
     }

I hope you are not claiming that these two scopes "terminate at the same 
point" in this case.

-- 
Best regards,
Andrey Tarasevich

[toc] | [prev] | [next] | [standalone]


#143045

FromKeith Thompson <kst-u@mib.org>
Date2019-04-11 17:08 -0700
Message-ID<lny34g13xb.fsf@kst-u.example.com>
In reply to#143042
Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
> On 4/11/2019 2:20 PM, James Kuyper wrote:
>> On Thursday, April 11, 2019 at 3:28:49 PM UTC-4, Andrey Tarasevich wrote:
[...]
>>> ... and thus allow declarations of
>>> identical identifiers. For example
>>>
>>>       for (int i = 0; i < 10; ++i)
>>>       {
>>>         int i = 42;
>>>       }
>>>
>>> In this case the inner `i` has its own nested scope and does not
>>> conflict with `i` from the cycle header.
>> 
>> I believe this is incorrect, though I can't test it with a compiler
>> right now. The scope of the first 'i' ends with the '}' at the end of
>> the loop; the same is true of the inner 'i'.
>
> This code is perfectly correct and actually fully agrees with what you 
> _yourself_ were stating above.
>
>> "Two identifiers have the _same scope_ if and only if their scopes
>> terminate at the same point." (6.2.1p6). The phrase "same scope" is
>> italicized, an ISO convention indicating that this clause is considered
>> to be the official definition of the italicized phrase.
>
> No. Nested blocks mean nested scopes in C. It does not matter whether 
> the block is "explicit" (a pair of `{}`) or "implicit" (as described 
> above). This immediately means that scopes associated with nested blocks 
> have different termination points.

What are these two different termination points?

Here's the code again:

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

There are two blocks here, one for the for statement and one for its
body.  N1570 6.8.5p5:

    An iteration statement is a block whose scope is a strict
    subset of the scope of its enclosing block. The loop body is
    also a block whose scope is a strict subset of the scope of
    the iteration statement.

One scope starts at the "for" keyword and ends at the "}" symbol.
The other scope starts at the "{" symbol and ends at the "}" symbol.
They start at different points, but they terminate at the same point.

> There's no difference between the above example and simple
>
>      {
>        int i = 42;
>        {
>          int i = 5;
>        }
>      }
>
> I hope you are not claiming that these two scopes "terminate at the same 
> point" in this case.

The difference is that in this case the two scopes terminate at
different points, the two "}" symbols, so they are not the "same scope".

I'd like to be wrong about this.  In the first example, I'd *like*
the declaration of the inner i, shadowing the outer i, to be legal
(though it's poor style).  At least gcc, clang, and tcc accept it,
and I would have assumed it was valid.  But given the somewhat
counterintuitive definition of "same scope", I'd say that in the
first example both i's have the same scope, and therefore the code
is invalid.

Do you argue that the two scopes don't end at the same point?  If so,
what exactly is a "point"?

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#143052

FromAndrey Tarasevich <andreytarasevich@hotmail.com>
Date2019-04-11 18:45 -0700
Message-ID<q8oqkc$ujb$1@dont-email.me>
In reply to#143045
On 4/11/2019 5:08 PM, Keith Thompson wrote:
>>
>> No. Nested blocks mean nested scopes in C. It does not matter whether
>> the block is "explicit" (a pair of `{}`) or "implicit" (as described
>> above). This immediately means that scopes associated with nested blocks
>> have different termination points.
> 
> What are these two different termination points?

The end of the "explicit" block and the end of the "implicit" block.

> Here's the code again:
> 
>      for (int i = 0; i < 10; ++i)
>      {
>          int i = 42;
>      }
> 
> There are two blocks here, one for the for statement and one for its
> body.  N1570 6.8.5p5:
> 
>      An iteration statement is a block whose scope is a strict
>      subset of the scope of its enclosing block. The loop body is
>      also a block whose scope is a strict subset of the scope of
>      the iteration statement.

Exactly. The word *strict* is used in this text to emphasize the fact 
that these blocks are nested, to emphasize the fact that they are different.

> One scope starts at the "for" keyword and ends at the "}" symbol.
> The other scope starts at the "{" symbol and ends at the "}" symbol.
> They start at different points, but they terminate at the same point.

No, they don't. For some reason you are attempting to attach beginning 
and termination points of blocks to some physical characters in the 
source code. The language defines no such attachment. Beginning and 
termination points are concepts. And two different blocks always have 
two different termination points. Pretty simple.

One might claim that the document is underdefined, but to me it looks 
like a stretch. The intent is perfectly clear: introduce some extra 
("implicit") blocks that envelop the existing blocks. These extra blocks 
are "invisible" but otherwise behave just like normal enveloping block do.

I don't see why anyone would interpret this wording an attempt to 
introduce into the language something as bizarre and foreign as 
"two-headed" blocks: blocks that begin at different points but terminate 
at the same one. Why? This is just... completely unnecessary in C.

-- 
Best regards,
Andrey Tarasevich

[toc] | [prev] | [next] | [standalone]


#143054

FromJames Kuyper <jameskuyper@alumni.caltech.edu>
Date2019-04-11 23:16 -0400
Message-ID<q8ovti$k7j$1@dont-email.me>
In reply to#143052
On 4/11/19 9:45 PM, Andrey Tarasevich wrote:
> On 4/11/2019 5:08 PM, Keith Thompson wrote:
>>>
>>> No. Nested blocks mean nested scopes in C. It does not matter whether
>>> the block is "explicit" (a pair of `{}`) or "implicit" (as described
>>> above). This immediately means that scopes associated with nested blocks
>>> have different termination points.
>>
>> What are these two different termination points?
> 
> The end of the "explicit" block and the end of the "implicit" block.
> 
>> Here's the code again:
>>
>>      for (int i = 0; i < 10; ++i)
>>      {
>>          int i = 42;
>>      }
>>
>> There are two blocks here, one for the for statement and one for its
>> body.  N1570 6.8.5p5:
>>
>>      An iteration statement is a block whose scope is a strict
>>      subset of the scope of its enclosing block. The loop body is
>>      also a block whose scope is a strict subset of the scope of
>>      the iteration statement.
> 
> Exactly. The word *strict* is used in this text to emphasize the fact 
> that these blocks are nested, to emphasize the fact that they are different.

Yes, the for statement has two subsets, "for (int i = 0; i < 10; ++i)"
and "{ int i = 42; }". The outer block is the union of those two
subsets, the inner block consists solely of the second subset.

>> One scope starts at the "for" keyword and ends at the "}" symbol.
>> The other scope starts at the "{" symbol and ends at the "}" symbol.
>> They start at different points, but they terminate at the same point.
> 
> No, they don't. For some reason you are attempting to attach beginning 
> and termination points of blocks to some physical characters in the 
> source code.

That might have something to do with the fact that the standard
identifies those blocks with statements. Statements are elements of the
grammar, ultimately composed of a series of tokens. Therefore, a block
is also composed of a series of tokens. It starts with the first token
is that series, and ends with the last token of that series.

 The language defines no such attachment. Beginning and
> termination points are concepts. And two different blocks always have 
> two different termination points. ...

That used to be the case, though that fact was never part of the
definition of a block, it was merely a fact that happened to be true
with the blocks that were defined in C90. It's no longer true of the
blocks defined in C99+.

> ... Pretty simple.>
> One might claim that the document is underdefined, but to me it looks 
> like a stretch. The intent is perfectly clear: introduce some extra 
> ("implicit") blocks that envelop the existing blocks. These extra blocks 
> are "invisible" but otherwise behave just like normal enveloping block do.

The blocks don't look the least bit invisible to me. I think they look
invisible to you only because you feel that a block should start and end
with matching curly brackets. That feeling leads you to surround the
entire for-statement in an extra matched pair of invisible curly
brackets - but that's not how the standard describes it. It merely says
that the entire iteration statement is a block; it doesn't say "the
entire iteration statement plus implicit curly brackets surrounding it
is a block".

As the standard describes it, the outer block is the entire for
statement: it starts with the for keyword, and extends to the end of the
statement whose execution is controlled by the for statement. The inner
block is that entire controlled sub-statement, no matter what form it takes.
Both blocks end at the same location: the end of the controlled
sub-statement. Therefore, all block scope identifiers in either block
have scopes that end in the same location, and therefore have the same
scope.

> I don't see why anyone would interpret this wording an attempt to 
> introduce into the language something as bizarre and foreign as 
> "two-headed" blocks: blocks that begin at different points but terminate 
> at the same one. Why? This is just... completely unnecessary in C.

It's the scopes that are the same, not the blocks. And multi-headed
scopes have been the norm, not the exception, in C:
"Structure, union, and enumeration tags have scope that begins just
after the appearance of the tag in a type specifier that declares the
tag. Each enumeration constant has scope that begins just after the
appearance of its defining enumerator in an enumerator list. Any other
identifier has scope that begins just after the completion of its
declarator." (6.2.1p7).
Therefore, no two identifiers have scopes that start in the same
location. But all identifiers that have scopes that end in the same
location are still considered to have the "same scope" for the purposes
of those rules that talk about "same scope".

[toc] | [prev] | [next] | [standalone]


#143055

FromKeith Thompson <kst-u@mib.org>
Date2019-04-11 20:19 -0700
Message-ID<lnpnpr29mg.fsf@kst-u.example.com>
In reply to#143052
Andrey Tarasevich <andreytarasevich@hotmail.com> writes:
> On 4/11/2019 5:08 PM, Keith Thompson wrote:
>>> No. Nested blocks mean nested scopes in C. It does not matter whether
>>> the block is "explicit" (a pair of `{}`) or "implicit" (as described
>>> above). This immediately means that scopes associated with nested blocks
>>> have different termination points.
>> 
>> What are these two different termination points?
>
> The end of the "explicit" block and the end of the "implicit" block.

There's nothing implicit about either block.  One block is the
iteration statement.  The other is the statement (a compound
statement) that is the loop body.  My best guess is that by
"implicit" you mean a block that's not delimited by opening and
closing curly braces, but nothing in the standard says that a block
is delimited by braces, or that a block that's not delimited by
braces is "implicit".

(In C90, "block" was synonymous with "compound statement", but
that was changed in C99.)

>> Here's the code again:
>> 
>>      for (int i = 0; i < 10; ++i)
>>      {
>>          int i = 42;
>>      }
>> 
>> There are two blocks here, one for the for statement and one for its
>> body.  N1570 6.8.5p5:
>> 
>>      An iteration statement is a block whose scope is a strict
>>      subset of the scope of its enclosing block. The loop body is
>>      also a block whose scope is a strict subset of the scope of
>>      the iteration statement.
>
> Exactly. The word *strict* is used in this text to emphasize the fact 
> that these blocks are nested, to emphasize the fact that they are different.

I think that's a stretch.

In the case of scopes, if the phrase "strict subset" is intended to
imply that the two scopes do not end at the same point, that's a poor
way to express that intent.

A scope is by definition a "region of program text" (6.2.1p2).  Do you
claim that the termination point of a scope is something other than the
end of that region of program text?

>> One scope starts at the "for" keyword and ends at the "}" symbol.
>> The other scope starts at the "{" symbol and ends at the "}" symbol.
>> They start at different points, but they terminate at the same point.
>
> No, they don't. For some reason you are attempting to attach beginning 
> and termination points of blocks to some physical characters in the 
> source code.

Yes, of course I am.  How else would you define them?  Blocks are
syntactic constructs consisting of sequences of tokens.  Both of these
blocks have the same last token.

>              The language defines no such attachment. Beginning and 
> termination points are concepts. And two different blocks always have 
> two different termination points. Pretty simple.

Proof by blatant assertion?  Can you explain what a "termination point"
is other than by calling it a "concept"?

> One might claim that the document is underdefined, but to me it looks 
> like a stretch. The intent is perfectly clear: introduce some extra 
> ("implicit") blocks that envelop the existing blocks. These extra blocks 
> are "invisible" but otherwise behave just like normal enveloping block do.
>
> I don't see why anyone would interpret this wording an attempt to 
> introduce into the language something as bizarre and foreign as 
> "two-headed" blocks: blocks that begin at different points but terminate 
> at the same one. Why? This is just... completely unnecessary in C.

I interpret it that way because that's what it says.

The definition of "same scope" goes back to C89/C90, and it worked fine
then.  It was intended to cover things like this:

    /* Example 1 */
    {
        int i = 10;
        int i = 20; /* invalid, "same scope" */
    }

When C99 added new kinds of blocks, and thus new rules for scopes, the
definition wasn't updated.  It seems clear to me that something like:

    /* Example 2 */
    for (int i = 0; i < 10; ++i) {
        int i;     /* Does this have the "same scope"? */
    }

is *intended* to be valid (as the authors of gcc, clang, and tcc appear
to have assumed), but the definition of "same scope" was not updated to
handle cases like this.

In Example 1 above both "i"s have the same innermost enclosing block,
and in Example 2 the two "i"s have different innermost enclosing blocks.
I think the definition of "same scope" needs to be updated to reflect
that (while still covering non-block scopes).

And a phrase other than "same scope" would be clearer -- perhaps
"colliding scopes"?

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

[toc] | [prev] | [next] | [standalone]


#143063

FromTim Rentsch <tr.17687@z991.linuxsc.com>
Date2019-04-12 14:39 -0700
Message-ID<86imvij43c.fsf@mailhub.linuxsc.com>
In reply to#143052
Andrey Tarasevich <andreytarasevich@hotmail.com> writes:

> On 4/11/2019 5:08 PM, Keith Thompson wrote:
>
>>> No.  Nested blocks mean nested scopes in C. It does not matter
>>> whether the block is "explicit" (a pair of `{}`) or "implicit" (as
>>> described above).  This immediately means that scopes associated
>>> with nested blocks have different termination points.
>>
>> What are these two different termination points?
>
> The end of the "explicit" block and the end of the "implicit"
> block.
>
>> Here's the code again:
>>
>>      for (int i = 0; i < 10; ++i)
>>      {
>>          int i = 42;
>>      }
>>
>> There are two blocks here, one for the for statement and one for
>> its body.  N1570 6.8.5p5:
>>
>>      An iteration statement is a block whose scope is a strict
>>      subset of the scope of its enclosing block.  The loop body is
>>      also a block whose scope is a strict subset of the scope of
>>      the iteration statement.
>
> Exactly.  The word *strict* is used in this text to emphasize the
> fact that these blocks are nested, to emphasize the fact that they
> are different.
>
>> One scope starts at the "for" keyword and ends at the "}" symbol.
>> The other scope starts at the "{" symbol and ends at the "}"
>> symbol.  They start at different points, but they terminate at the
>> same point.
>
> No, they don't.  For some reason you are attempting to attach
> beginning and termination points of blocks to some physical
> characters in the source code.  The language defines no such
> attachment.  Beginning and termination points are concepts.  And
> two different blocks always have two different termination points.
> Pretty simple.
>
> One might claim that the document is underdefined, but to me it
> looks like a stretch.  The intent is perfectly clear:  introduce
> some extra ("implicit") blocks that envelop the existing blocks.
> These extra blocks are "invisible" but otherwise behave just like
> normal enveloping block do.

I agree with your reading:  the block of a controlling statement
has a different (and larger) scope than those of its controlled
statement(s).  Note further that in C11 a small change in wording
was made regarding scopes.  In C99, 6.2.1 p4 said in part (italics
in original in all cases)

    If so, the scope of one entity (the /inner scope/) will be a
    strict subset of the scope of the other entity (the /outer
    scope/).

In C11, this sentence was changed to say

    If so, the scope of one entity (the /inner scope/) will end
    strictly before the scope of the other entity (the /outer
    scope/).

IMO this change clarifies the "strict subset" wording used
in the text in section 6.8 (Statements).

Incidentally, note that the rules regarding "induced" blocks
and their scopes are slightly different in C than in C++.  For
example, take the following function:

    int
    foo(){
        extern unsigned bas();
        for(  unsigned x = bas();  x;  x /= 2  ){
            int x = 2;
            /* ... potentially more code here ... */
        }
        return  0;
    }

In C, this function definition is legal.  In C++, the 'int x;'
gets flagged as a redeclaration of 'x', even though it is
enclosed in a compound statement.  To me it looks like the C
way is better.  For one thing, this pattern can be used to
guarantee that no modifications happen to the loop control
variable within the loop body.  Also I think the C++ way
runs afoul of the Law of Least Astonishment.

[toc] | [prev] | [next] | [standalone]


#143076

FromIke Naar <ike@sdf.lonestar.org>
Date2019-04-13 20:02 +0000
Message-ID<slrn3r24qb4g3a.6u9.ike@sdf.lonestar.org>
In reply to#143052
On 2019-04-12, Andrey Tarasevich <andreytarasevich@hotmail.com> wrote:
> On 4/11/2019 5:08 PM, Keith Thompson wrote:
>>>
>>> No. Nested blocks mean nested scopes in C. It does not matter whether
>>> the block is "explicit" (a pair of `{}`) or "implicit" (as described
>>> above). This immediately means that scopes associated with nested blocks
>>> have different termination points.
>> 
>> What are these two different termination points?
>
> The end of the "explicit" block and the end of the "implicit" block.
>
>> Here's the code again:
>> 
>>      for (int i = 0; i < 10; ++i)
>>      {
>>          int i = 42;
>>      }
>> 
>> There are two blocks here, one for the for statement and one for its
>> body.  N1570 6.8.5p5:
>> 
>>      An iteration statement is a block whose scope is a strict
>>      subset of the scope of its enclosing block. The loop body is
>>      also a block whose scope is a strict subset of the scope of
>>      the iteration statement.
>
> Exactly. The word *strict* is used in this text to emphasize the fact 
> that these blocks are nested, to emphasize the fact that they are different.

Set A is a strict subset of set B if
-  A is a subset of B, and
-  A is not equal to B.
(if A were equal to B, A would still be a subset of B, but not a strict subset)

In the realm of sets of integers, {2,3} is a strict subset of {1,2,3},
even though both sets have the same maximum.

Likewise, scope A can be a strict subset of scope B even when both
scopes end at the same point. It's sufficient when A starts later than B.

[toc] | [prev] | [next] | [standalone]


Page 2 of 3 — ← Prev page 1 [2] 3  Next page →

Back to top | Article view | comp.lang.c


csiph-web