May 27, 2004
This proposition has merit. Instead of advocating "const" to the language (like a lot of people, including myself until I started to doubt myself, have done) it extends the in/out/inout concept so that it would be semantically as strong as "const" is in C/C++.

Just adding const would make the language clunky and complicated; implementing this proposition might be just the right solution.

(There are of course the troubles that come with const as well, I think. Something about overload resolution or something. Anyway, it's been one argument against const.)

-Antti

In article <c94adp$20fe$1@digitaldaemon.com>, Arcane Jill wrote:
> I have given a lot of thought to the whole "const" thing, and it seems to me that there is a way around this that will keep everybody happy. (It was not I who thought of this, by the way - I'm just clarifying).

[...]

> (3) As part of Design-By-Contract, to specify a contract that a function will
> not modify something

[...]

> This brings us to the can of worms that is definition (3). I believe we can do this. I believe it can work. Here's how:
> 
> The CURRENT situation is that function parameters may be declared in three different ways. These are:
> 
> (i)   in (the default)
> (ii)  out
> (iii) inout
> 
> To implement DbC-readonly assertions, we actually need FOUR states. For consistency, these should be:
> 
> (i)   (default)
> (ii)  in
> (iii) out
> (iv)  inout
> 
> (default) is what you get if you specify none of the other keywords. For DbC-readonly to work, we only need to state that out, inout, and default, shall work exactly as before (current behavior is unchanged), but new behavior needs to be defined for anything declared using "in". I shall describe this new behavior below.
> 
> Note though that in addition to its placement before formal function paramters, "in" must also be placeable before member function declarations, like this:
> 
>>       class A
>>       {
>>           in void f()         // a contract that the function will not modify any of A's member variables
>>           {
>>               // do stuff
>>           }
>>       }
> 
> For reference, the C++ syntax for doing this is:
> 
>>       class A
>>       {
>>           void f() const      // a contract that the function will not modify any of A's member variables
>>           {
>>               // do stuff
>>           }
>>       };
> 
> Above, I talked about a difference in behavior. The important question is HOW this is going to work? Specifically, what gets passed where? How can the compiler spot a contract violation? These questions I shall answer, now.
> 
> To the first point, parameters are passed exactly as they are now. Current behavior is suffient.
> 
> To the second point, given the declaration:
> 
>>       void f(in SomeObject a)
> 
> the following would all be compile-time errors if they occur within the body of the function:
> 
> (1)     a = expression;     // illegal always
> (2)     a.m = expression;   // illegal always
> (3)     a.g();              // illegal unless g() was declared as in void g()
> (4)     g(a)                // illegal unless g() was declared as void g(in
> SomeObject a)
> 
> These tests are sufficient to catch all contract violations at compile-time. (The same tests could also be used to verify that "final" objects are not modified).
> 
> Incidently, the statement:
> 
>>       this = expression;
> 
> should /always/ be illegal, regardless of whether or not the function was declared with "in". Note that currently, that statement is allowed, The consequences of executing it are undefined.
> 
> CONSEQUENCES
> 
> If we were starting from scratch, there would be no consequences. But we are not starting from scratch. We already have a large body of code out there, much of which will not immediately compile under this proposed change.
> 
> There is an immediate consequence for getter functions (properties). All property getter functions would have to be redclared, like this:
> 
>>       int x() { return x_; }      // BEFORE
>>       in int x() { return x_; }   // AFTER
> 
> Similarly, there is an immediate consequence for the non-assigning operator overloads, which would also have to be redeclared:
> 
>>       SomeObject opAdd(SomeObject x)          // BEFORE
>>       in SomeObject opAdd(in SomeObject x)    // AFTER
> 
> Then, similarly, you redeclare every other function that requires it, in every file, least-dependent (that is, most low-level) file first. The absolute simplest way to do this is to declare EVERYTHING that isn't "out" or "inout" as "in", and then remove the "in" keyword from things that won't compile. Providing you attack the source code in the right order, this should convert everything that needs it. And it might even show up a few bugs that got missed along the way!
> 
> CONCLUSION
> 
> This is achieveable, and simple. The remaining questions are: (i) does the D community want this, given that recoding of a lot of source files will be necessary, and (ii) how does Walter feel about implementing it, given that the illegalities would be relatively easy to spot?
> 
> OH YEAH - I FORGOT
> 
> Given the declaration:
> 
>>       f(out SomeType x)
> 
> I would *assume* (though I haven't done the experiment) that either the caller or the callee overwrites any former value of x with the init value of SomeType, before doing anything with x. If it doesn't, it should.
> 
> 
> Arcane Jill


-- 
I will not be using Plan 9 in the creation of weapons of mass destruction to be used by nations other than the US.
May 27, 2004
I'd like to attempt a further clarification regarding the three types of 'const' usage identified:

1) compile-time constants, such as enum. You cannot take the address of these.

2) final instances of arrays, structs, literals, objects, whatever. The memory occupied by these should likely be initialized at the point of declaration, and cannot be changed once set. It is conceivable (and often necessary) to take the address of these, and it might be useful to think of them as ROM candidates. Note that final objects might be a bit tricky.

3) DbC constraints that prevent an argument from being internally modified, either wholly or in specific ways. In addition, there's the issue of pass-by-value/pass-by-reference for structs that muddies the keyword water somewhat within this arena.

- Kris


"Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:c94adp$20fe$1@digitaldaemon.com...
> I have given a lot of thought to the whole "const" thing, and it seems to
me
> that there is a way around this that will keep everybody happy. (It was
not I
> who thought of this, by the way - I'm just clarifying).
>
> Summary - the story so far:
> Three uses of const have been identified in C++
> (1) To declare a complile-time constant
> (2) To declare an instance of something which needs to be stored in ROM
> (3) As part of Design-By-Contract, to specify a contract that a function
will
> not modify something
>
> The keyword "const" is implemented in D to allow use (1) only.
>
> It has been suggested that the distinction between (1) and (2) could be
decided
> by the compiler. Alas, that may not be so. Given the declaration:
>
> >       const int FORTY_TWO = 42;
>
> the expression (&FORTY_TWO) should be illegal in case (1), but legal in
case
> (2). Therefore, if we wish the language to support option (2), we need a
need a
> new keyword. I suggest re-using "final". Thus:
>
> >       const int FORTY_TWO = 42;       // Not stored anywhere, therefore
cannot take address
> >       final int FORTY_TWO = 42;       // Stored in ROM, so address can
be taken, but value may not be changed
>
> This brings us to the can of worms that is definition (3). I believe we
can do
> this. I believe it can work. Here's how:
>
> The CURRENT situation is that function parameters may be declared in three different ways. These are:
>
> (i)   in (the default)
> (ii)  out
> (iii) inout
>
> To implement DbC-readonly assertions, we actually need FOUR states. For consistency, these should be:
>
> (i)   (default)
> (ii)  in
> (iii) out
> (iv)  inout
>
> (default) is what you get if you specify none of the other keywords. For DbC-readonly to work, we only need to state that out, inout, and default,
shall
> work exactly as before (current behavior is unchanged), but new behavior
needs
> to be defined for anything declared using "in". I shall describe this new behavior below.
>
> Note though that in addition to its placement before formal function
paramters,
> "in" must also be placeable before member function declarations, like
this:
>
> >       class A
> >       {
> >           in void f()         // a contract that the function will not
modify any of A's member variables
> >           {
> >               // do stuff
> >           }
> >       }
>
> For reference, the C++ syntax for doing this is:
>
> >       class A
> >       {
> >           void f() const      // a contract that the function will not
modify any of A's member variables
> >           {
> >               // do stuff
> >           }
> >       };
>
> Above, I talked about a difference in behavior. The important question is
HOW
> this is going to work? Specifically, what gets passed where? How can the compiler spot a contract violation? These questions I shall answer, now.
>
> To the first point, parameters are passed exactly as they are now. Current behavior is suffient.
>
> To the second point, given the declaration:
>
> >       void f(in SomeObject a)
>
> the following would all be compile-time errors if they occur within the
body of
> the function:
>
> (1)     a = expression;     // illegal always
> (2)     a.m = expression;   // illegal always
> (3)     a.g();              // illegal unless g() was declared as in void
g()
> (4)     g(a)                // illegal unless g() was declared as void
g(in
> SomeObject a)
>
> These tests are sufficient to catch all contract violations at
compile-time.
> (The same tests could also be used to verify that "final" objects are not
> modified).
>
> Incidently, the statement:
>
> >       this = expression;
>
> should /always/ be illegal, regardless of whether or not the function was declared with "in". Note that currently, that statement is allowed, The consequences of executing it are undefined.
>
> CONSEQUENCES
>
> If we were starting from scratch, there would be no consequences. But we
are not
> starting from scratch. We already have a large body of code out there,
much of
> which will not immediately compile under this proposed change.
>
> There is an immediate consequence for getter functions (properties). All property getter functions would have to be redclared, like this:
>
> >       int x() { return x_; }      // BEFORE
> >       in int x() { return x_; }   // AFTER
>
> Similarly, there is an immediate consequence for the non-assigning
operator
> overloads, which would also have to be redeclared:
>
> >       SomeObject opAdd(SomeObject x)          // BEFORE
> >       in SomeObject opAdd(in SomeObject x)    // AFTER
>
> Then, similarly, you redeclare every other function that requires it, in
every
> file, least-dependent (that is, most low-level) file first. The absolute simplest way to do this is to declare EVERYTHING that isn't "out" or
"inout" as
> "in", and then remove the "in" keyword from things that won't compile.
Providing
> you attack the source code in the right order, this should convert
everything
> that needs it. And it might even show up a few bugs that got missed along
the
> way!
>
> CONCLUSION
>
> This is achieveable, and simple. The remaining questions are: (i) does the
D
> community want this, given that recoding of a lot of source files will be necessary, and (ii) how does Walter feel about implementing it, given that
the
> illegalities would be relatively easy to spot?
>
> OH YEAH - I FORGOT
>
> Given the declaration:
>
> >       f(out SomeType x)
>
> I would *assume* (though I haven't done the experiment) that either the
caller
> or the callee overwrites any former value of x with the init value of
SomeType,
> before doing anything with x. If it doesn't, it should.
>
>
> Arcane Jill
>
>
>


May 28, 2004
On Thu, 27 May 2004 08:55:21 +0000 (UTC), Arcane Jill wrote:

> I have given a lot of thought to the whole "const" thing, and it seems to me that there is a way around this that will keep everybody happy. (It was not I who thought of this, by the way - I'm just clarifying).
> 
> Summary - the story so far:
> Three uses of const have been identified in C++
> (1) To declare a complile-time constant
> (2) To declare an instance of something which needs to be stored in ROM
> (3) As part of Design-By-Contract, to specify a contract that a function will
> not modify something
> 
> The keyword "const" is implemented in D to allow use (1) only.

So this idea is a LITERAL value that does not take up any RAM at runtime, right? If so, maybe the keyword 'literal' could be used instead of 'const'.

      literal FORTY_TWO = 42;

Note that the data type information is redundant as it is always the same datatype as the RHS expression.

      literal PI = 3.1415926535897932384626433832795;

Of course if we had array and structure literals this would be nice too.
      struct Point{ int X,Y;};
      literal STARTPOINT = Point(X=5,Y=10);
but let's not get ahead of ourselves ;-)


-- 
Derek
28/May/04 10:06:44 AM
May 28, 2004
In article <c94nff$e11$1@digitaldaemon.com>, Hauke Duden says... ..
>> The problem occurs if "const int FORTY_TWO = 42;" occurs in module A, but the expression "&FORTY_TWO" occurs in module B. When compiling module A, how is the compiler to know (without clairvoyance) what module B is going to do? It can't optimize FORTY_TWO away if there's even a /chance/ that some other module might take its address. Yet ... we want it to make that optimization. Don't we?
>
>I always thought this kind of optimization (throwing away unused entities) is done by the linker. Isn't it?
>
>Anyway, I don't think that having some additional integers floating around would be any problem. The additional overhead certainly does not justify introducing two different kinds of const keywords. And if it is a problem for a specialized application you could still let the compiler compile all modules at once.
>
>For me this smells like the "register" keyword all over again.
>
>Hauke

In C++ at least, the linker throws away compilation units (files) when no references to them are found.  Anything smaller than that is probably not removed by the linker.  I spent all day today (at work) moving stuff between compilation units to reduce executable sizes.

Compiler experts:

What do you think of breaking compilation units up into the smallest possible pieces in the compiler?  If the compiler emitted one .o file for each function or method (optionally) and automatically bound them into a .a file for me, I would only pull in methods that were actually called; the recursive effect of this could reduce file sizes massively.

I reduced a C++ executable by 30MB by doing this by hand, and I only removed dependency on one library, so I think it could help all-around.  (Yes this was debug mode, our release mode binaries are not that large).

Kevin


May 28, 2004
>What do you think of breaking compilation units up into the smallest possible pieces in the compiler?  If the compiler emitted one .o file for each function or method (optionally) and automatically bound them into a .a file for me, I would only pull in methods that were actually called; the recursive effect of this could reduce file sizes massively.

I think this is one of the things I mentioned when I first heard about D. I got into the habit of putting only one function in each file when I worked on a large team using C, and that can be done with C++ too (if I recall correctly). I miss that with C#, but I understand version 2 of C# will allow breaking a class up over several files. Tiny code files offer several benefits to teams.

However, I believe the compiler/linker shouldn't do it _for_ you.


May 28, 2004
In article <c97r1k$1v9u$1@digitaldaemon.com>, Juan C says...
>
>>What do you think of breaking compilation units up into the smallest possible pieces in the compiler?  If the compiler emitted one .o file for each function or method (optionally) and automatically bound them into a .a file for me, I would only pull in methods that were actually called; the recursive effect of this could reduce file sizes massively.
>
>I think this is one of the things I mentioned when I first heard about D. I got into the habit of putting only one function in each file when I worked on a large team using C, and that can be done with C++ too (if I recall correctly). I miss that with C#, but I understand version 2 of C# will allow breaking a class up over several files. Tiny code files offer several benefits to teams.
>
>However, I believe the compiler/linker shouldn't do it _for_ you.

Out of curiosity, why not?  Doing this manually takes a lot of effort, and has no benefit, except for the dubious C-language stoicism of having walked the whole distance on foot and not gotten eaten by a bear (yet).

What I want is dependency information to be done for "program elements", be they functions, methods, static strings, or global/module scope data objects.  Then only the necessary parts of the code will need to be included.

The only (functional) downside I see is the inability to write functions that
can only be run from GDB via "call x(4)".

Kevin


May 29, 2004
In article <c96kgq$651$1@digitaldaemon.com>, Kevin Bealer says...
>
>Compiler experts:

I don't claim to be a compiler expert, but...

>What do you think of breaking compilation units up into the smallest possible pieces in the compiler?  If the compiler emitted one .o file for each function or method (optionally) and automatically bound them into a .a file for me, I would only pull in methods that were actually called; the recursive effect of this could reduce file sizes massively.
>
>I reduced a C++ executable by 30MB by doing this by hand, and I only removed dependency on one library, so I think it could help all-around.  (Yes this was debug mode, our release mode binaries are not that large).

..there is a problem doing this in D. The way it's designed. private stuff has to be in the same *file* as anything which references it, and there is no package level attribute. So we can't even implement the same level of granularity to which we are accustomed in C and C++.

For this, and other reasons, I am now convinced there is a stong case for the "package" attribute to exist in D.

Arcane Jill


May 30, 2004
In article <c99ls1$1lmi$1@digitaldaemon.com>, Arcane Jill says...
>
>In article <c96kgq$651$1@digitaldaemon.com>, Kevin Bealer says...
>>
>>Compiler experts:
>
>I don't claim to be a compiler expert, but...
>
>>What do you think of breaking compilation units up into the smallest possible pieces in the compiler?  If the compiler emitted one .o file for each function or method (optionally) and automatically bound them into a .a file for me, I would only pull in methods that were actually called; the recursive effect of this could reduce file sizes massively.
>>
>>I reduced a C++ executable by 30MB by doing this by hand, and I only removed dependency on one library, so I think it could help all-around.  (Yes this was debug mode, our release mode binaries are not that large).
>
>..there is a problem doing this in D. The way it's designed. private stuff has to be in the same *file* as anything which references it, and there is no package level attribute. So we can't even implement the same level of granularity to which we are accustomed in C and C++.
>
>For this, and other reasons, I am now convinced there is a stong case for the "package" attribute to exist in D.
>
>Arcane Jill

I agree, there is a problem with doing it using user-level tools before the compile, but once in the compiler, it could be done in D, C, or C++.  Compilers enforce the access attributes at the source level.

If it rejects the usage, it produces an error message.  If it accepts the usage, then it would still be free to write multiple objects.

In C or C++, this change would extend the benefit of using one-file-per-package to all programs, even those that put all code in one file.

In D, it may be less beneficial because the virtual table would probably pull in all the .o files, unless the given functions were "final".

If you need the benefit in D, you could use heroic measures to get it, by writing classes like this:

Module A:

class Foo {
final uint increase(int Z)
{
return impl_Foo_increase(x,y,Z);
}

final uint decrease(int W)
{
return impl_Foo_decrease(x,y,W);
}

private:
int x,y;
};

Module B:

uint impl_Foo_increase(inout uint x, inout uint y, inout Z)
{
.. some code
}

Module C:

uint impl_Foo_decrease(inout uint x, inout uint y, inout W)
{
.. some code
}

I know, its a bit ugly.  This would also be a way to write C/C++ or C/C++/D programs.  (Uh oh -- what have I unleased?  Please burn this email!)  There are probably people doing this already of course, but without the "final"ity.

Kevin


June 04, 2004
Interesting. I look forward to hearing what Walter thinks of this, but I'm going to keep breathing while I wait. <G>

"Arcane Jill" <Arcane_member@pathlink.com> wrote in message news:c94adp$20fe$1@digitaldaemon.com...
> I have given a lot of thought to the whole "const" thing, and it seems to me that there is a way around this that will keep everybody happy. (It was not I who thought of this, by the way - I'm just clarifying).
>
> Summary - the story so far:
> Three uses of const have been identified in C++
> (1) To declare a complile-time constant
> (2) To declare an instance of something which needs to be stored in ROM
> (3) As part of Design-By-Contract, to specify a contract that a function will
> not modify something
>
> The keyword "const" is implemented in D to allow use (1) only.
>
> It has been suggested that the distinction between (1) and (2) could be decided
> by the compiler. Alas, that may not be so. Given the declaration:
>
> >       const int FORTY_TWO = 42;
>
> the expression (&FORTY_TWO) should be illegal in case (1), but legal in case
> (2). Therefore, if we wish the language to support option (2), we need a need a
> new keyword. I suggest re-using "final". Thus:
>
> >       const int FORTY_TWO = 42;       // Not stored anywhere, therefore
cannot take address
> >       final int FORTY_TWO = 42;       // Stored in ROM, so address can be
taken, but value may not be changed
>
> This brings us to the can of worms that is definition (3). I believe we can do this. I believe it can work. Here's how:
>
> The CURRENT situation is that function parameters may be declared in three different ways. These are:
>
> (i)   in (the default)
> (ii)  out
> (iii) inout
>
> To implement DbC-readonly assertions, we actually need FOUR states. For consistency, these should be:
>
> (i)   (default)
> (ii)  in
> (iii) out
> (iv)  inout
>
> (default) is what you get if you specify none of the other keywords. For DbC-readonly to work, we only need to state that out, inout, and default, shall work exactly as before (current behavior is unchanged), but new behavior needs to be defined for anything declared using "in". I shall describe this new behavior below.
>
> Note though that in addition to its placement before formal function paramters, "in" must also be placeable before member function declarations, like this:
>
> >       class A
> >       {
> >           in void f()         // a contract that the function will not modify
any of A's member variables
> >           {
> >               // do stuff
> >           }
> >       }
>
> For reference, the C++ syntax for doing this is:
>
> >       class A
> >       {
> >           void f() const      // a contract that the function will not modify
any of A's member variables
> >           {
> >               // do stuff
> >           }
> >       };
>
> Above, I talked about a difference in behavior. The important question is HOW this is going to work? Specifically, what gets passed where? How can the compiler spot a contract violation? These questions I shall answer, now.
>
> To the first point, parameters are passed exactly as they are now. Current behavior is suffient.
>
> To the second point, given the declaration:
>
> >       void f(in SomeObject a)
>
> the following would all be compile-time errors if they occur within the body of the function:
>
> (1)     a = expression;     // illegal always
> (2)     a.m = expression;   // illegal always
> (3)     a.g();              // illegal unless g() was declared as in void g()
> (4)     g(a)                // illegal unless g() was declared as void g(in
> SomeObject a)
>
> These tests are sufficient to catch all contract violations at compile-time. (The same tests could also be used to verify that "final" objects are not modified).
>
> Incidently, the statement:
>
> >       this = expression;
>
> should /always/ be illegal, regardless of whether or not the function was declared with "in". Note that currently, that statement is allowed, The consequences of executing it are undefined.
>
> CONSEQUENCES
>
> If we were starting from scratch, there would be no consequences. But we are
not
> starting from scratch. We already have a large body of code out there, much of which will not immediately compile under this proposed change.
>
> There is an immediate consequence for getter functions (properties). All property getter functions would have to be redclared, like this:
>
> >       int x() { return x_; }      // BEFORE
> >       in int x() { return x_; }   // AFTER
>
> Similarly, there is an immediate consequence for the non-assigning operator overloads, which would also have to be redeclared:
>
> >       SomeObject opAdd(SomeObject x)          // BEFORE
> >       in SomeObject opAdd(in SomeObject x)    // AFTER
>
> Then, similarly, you redeclare every other function that requires it, in every file, least-dependent (that is, most low-level) file first. The absolute simplest way to do this is to declare EVERYTHING that isn't "out" or "inout" as "in", and then remove the "in" keyword from things that won't compile.
Providing
> you attack the source code in the right order, this should convert everything that needs it. And it might even show up a few bugs that got missed along the way!
>
> CONCLUSION
>
> This is achieveable, and simple. The remaining questions are: (i) does the D community want this, given that recoding of a lot of source files will be necessary, and (ii) how does Walter feel about implementing it, given that the illegalities would be relatively easy to spot?
>
> OH YEAH - I FORGOT
>
> Given the declaration:
>
> >       f(out SomeType x)
>
> I would *assume* (though I haven't done the experiment) that either the caller or the callee overwrites any former value of x with the init value of SomeType, before doing anything with x. If it doesn't, it should.
>
>
> Arcane Jill
>
>
>


1 2
Next ›   Last »