Jump to page: 1 2
Thread overview
[Bug 191] New: Cannot refer to member variable in default value for method parameter
Jun 11, 2006
d-bugmail
Jun 11, 2006
Deewiant
Jun 11, 2006
Deewiant
Jun 12, 2006
BCS
Jun 12, 2006
Oskar Linde
Jun 13, 2006
Bruno Medeiros
Jun 13, 2006
BCS
Jun 14, 2006
Don Clugston
Jun 18, 2006
Bruno Medeiros
Jun 23, 2006
Don Clugston
Jun 23, 2006
Bruno Medeiros
Jun 23, 2006
BCS
Jun 12, 2006
Bruno Medeiros
Jun 12, 2006
Deewiant
June 11, 2006
http://d.puremagic.com/bugzilla/show_bug.cgi?id=191

           Summary: Cannot refer to member variable in default value for
                    method parameter
           Product: D
           Version: 0.160
          Platform: PC
        OS/Version: Windows
            Status: NEW
          Keywords: rejects-valid
          Severity: normal
          Priority: P3
         Component: DMD
        AssignedTo: bugzilla@digitalmars.com
        ReportedBy: deewiant@gmail.com


--
class c {
        int x = 5;
        int foo(int a = x) { return a; }
}

void main() {
        assert ((new c).foo() == 5);
}
--

The above code does not compile, producing the error "need 'this' to access member x".

Obliging by changing "int a = x" to "int a = this.x" changes the error to "'this' is only allowed in non-static member functions" followed by "this for x needs to be type c not type int".

It can be worked around by removing the default value and manually overriding
foo with "int foo() { return foo(x); }", but this is a needless annoyance and
precisely what default values are there for.

It might be worth noting that this is a longstanding bug which bit me for the first time with DMD 0.128: http://www.digitalmars.com/d/archives/digitalmars/D/bugs/4532.html


-- 

June 11, 2006
<d-bugmail@puremagic.com> wrote in message news:bug-191-

> It might be worth noting that this is a longstanding bug which bit me for
> the
> first time with DMD 0.128:
> http://www.digitalmars.com/d/archives/digitalmars/D/bugs/4532.html

Is this even in the spec?  I had no idea you were supposed to be allowed to use members as default parameters.


June 11, 2006
Jarrett Billingsley wrote:
> Is this even in the spec?  I had no idea you were supposed to be allowed to use members as default parameters.
> 

All I can find about default parameters in the spec is at the "Functions" page, where they pop out of the blue in the phrase "A function parameter's default value is not inherited", which is all that's said about them. The changelog for DMD 0.92 also says "Added default arguments to function parameters. Semantics are like C++." but that's it.

After some testing, it seems that that would actually explain it: C++ doesn't allow it.

For what it's worth, I don't see any sense in this restriction - how is a class's member different from any other variable in this situation? Especially since the workaround is so simple, and isn't that all that default parameters are meant to do - overload the method into a form which takes one less parameter and passes the default value to the original? They don't have to implement it that way, but that's how I see the semantics of it. And I'm not a compiler writer, but it doesn't seem very difficult to implement either.
June 11, 2006
"Deewiant" <deewiant.doesnotlike.spam@gmail.com> wrote in message news:e6hp54$a2d$1@digitaldaemon.com...

> For what it's worth, I don't see any sense in this restriction - how is a class's member different from any other variable in this situation?

Oh, wow, I didn't know that it allowed you to do this with other kinds of variables.  I always thought that the default parameter initializer had to evaluate to a constant!

> Especially
> since the workaround is so simple, and isn't that all that default
> parameters
> are meant to do - overload the method into a form which takes one less
> parameter
> and passes the default value to the original?

Well, as far as I know, that's not how the compiler implements it.  It just keeps one copy of the function, and when it comes across a call that doesn't have all the parameters, it rewrites the function call so that the unwritten parameters use the parameter initializer.  So there's no actual overloading going on.

> They don't have to implement it
> that way, but that's how I see the semantics of it.

That might be the problem - depending on whether or not the default argument was a member variable, it might end up adding complexity to the default argument mechanism, since the value of a local variable can't really be determined until _after_ the function has been called with a 'this' reference.  It would be like trying to do something like:

void func(int[] x, int y = x[5])

Since x[5] can't be determined until after func() has been called, it would have to have multiple code paths for func; one which would be for taking a y, and one which would determine y from the x argument.

Though it could possibly be added into the current mechanism, if you had a class like:

class A
{
    int x;

    void func(int y = x)
    {
        writefln(y);
    }
}

Then if you called func:

A a = new A;
a.func();

The compiler would know that the default initializer for the first argument is the member variable x, and would then rewrite the call as:

a.func(a.x);

Possible, but kind of complicated.

Overloading a function to implement the default parameter with a member variable as the initializer seems like a simple enough workaround until (if?) this is fixed.  I agree that it should be possible for consistency with the ability to use other variables as default argument initializers, but I don't know how many extra levels of complexity it adds.


June 11, 2006
Jarrett Billingsley wrote:
> "Deewiant" <deewiant.doesnotlike.spam@gmail.com> wrote in message news:e6hp54$a2d$1@digitaldaemon.com...
>> Especially since the workaround is so simple, and isn't that all that default parameters are meant to do - overload the method into a form which takes one less parameter and passes the default value to the original?
> 
> Well, as far as I know, that's not how the compiler implements it.  It just
> keeps one copy of the function, and when it comes across a call that doesn't
>  have all the parameters, it rewrites the function call so that the unwritten
>  parameters use the parameter initializer.  So there's no actual overloading
>  going on.
> 

If it does it like that - which _would_ be simpler and possibly faster for the non-class-member case - I can sort of see why it might be tricky for the class-member case.

But it's still not impossible, like you pointed out: calling a method, the compiler knows which class instance's method is being called and can take the member from the same instance.

It does, however, become impossible if the member is private or protected, and the caller can't access the variable. Which, come to think of it, is probably why it's not allowed in C++.

Simulating that extra overloaded method does work even in that case, though.
June 12, 2006
Deewiant wrote:
> Jarrett Billingsley wrote:
> 
>>Is this even in the spec?  I had no idea you were supposed to be allowed to use members as default parameters. 
>>
> 
> 
> All I can find about default parameters in the spec is at the "Functions" page,
> where they pop out of the blue in the phrase "A function parameter's default
> value is not inherited", which is all that's said about them. The changelog for
> DMD 0.92 also says "Added default arguments to function parameters. Semantics
> are like C++." but that's it.
[...]

Is this correct behavior? If it is it needs to be documeted better.

<code>
import std.stdio;

class C		{ int foo(int x = 1){return x;} }
class D:C	{ int foo(int x = 2){return x;} }


void main()
{
	C c = new D;
	D d = cast(D)c;

	writef(c.foo,\n);	// prints 1 (?)
	writef(d.foo,\n);	// prints 2
}
</code>
June 12, 2006
BCS skrev:
> Deewiant wrote:
>> Jarrett Billingsley wrote:
>>
>>> Is this even in the spec?  I had no idea you were supposed to be allowed to use members as default parameters.
>>
>>
>> All I can find about default parameters in the spec is at the "Functions" page,
>> where they pop out of the blue in the phrase "A function parameter's default
>> value is not inherited", which is all that's said about them. The changelog for
>> DMD 0.92 also says "Added default arguments to function parameters. Semantics
>> are like C++." but that's it.
> [...]
> 
> Is this correct behavior? If it is it needs to be documeted better.
> 
> <code>
> import std.stdio;
> 
> class C        { int foo(int x = 1){return x;} }
> class D:C    { int foo(int x = 2){return x;} }
> 
> 
> void main()
> {
>     C c = new D;
>     D d = cast(D)c;
> 
>     writef(c.foo,\n);    // prints 1 (?)
>     writef(d.foo,\n);    // prints 2
> }
> </code>

I guess this is the intended behavior. The default argument is evaluated at call site and passed to the function. main() calling c.foo has no way of knowing (at compile time) what derived class instance c may hold (there is no vtbl for default arguments).

I see how this can lead to hard to find bugs. I.e. if the default argument is changed in the base class, but not in a (maybe independently developed) derived class. It may be better if D inherited default values and forbade them to be redefined in derived classes.

/Oskar
June 12, 2006
Deewiant wrote:
> Jarrett Billingsley wrote:
>> Is this even in the spec?  I had no idea you were supposed to be allowed to use members as default parameters. 
>>
> 
> All I can find about default parameters in the spec is at the "Functions" page,
> where they pop out of the blue in the phrase "A function parameter's default
> value is not inherited", which is all that's said about them. The changelog for
> DMD 0.92 also says "Added default arguments to function parameters. Semantics
> are like C++." but that's it.
> 
> After some testing, it seems that that would actually explain it: C++ doesn't
> allow it.
> 

Which then means this is not a bug.
What shall it be now? (dropped issue?, documentation clarification request?, enhancement request?)

-- 
Bruno Medeiros - CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
June 12, 2006
Bruno Medeiros wrote:
> Deewiant wrote:
>> Jarrett Billingsley wrote:
>>> Is this even in the spec?  I had no idea you were supposed to be allowed to use members as default parameters.
>> 
>> All I can find about default parameters in the spec is at the "Functions" page, where they pop out of the blue in the phrase "A function parameter's default value is not inherited", which is all that's said about them. The changelog for DMD 0.92 also says "Added default arguments to function parameters. Semantics are like C++." but that's it.
>> 
>> After some testing, it seems that that would actually explain it: C++ doesn't allow it.
>> 
> 
> Which then means this is not a bug. What shall it be now? (dropped issue?, documentation clarification request?, enhancement request?)
> 

If you ask me, it's both of the latter. The documentation certainly needs improvement in this regard, and I certainly want to be able to use class members as default parameters. <g>
June 13, 2006
BCS wrote:
> Deewiant wrote:
>> Jarrett Billingsley wrote:
>>
>>> Is this even in the spec?  I had no idea you were supposed to be allowed to use members as default parameters.
>>
>>
>> All I can find about default parameters in the spec is at the "Functions" page,
>> where they pop out of the blue in the phrase "A function parameter's default
>> value is not inherited", which is all that's said about them. The changelog for
>> DMD 0.92 also says "Added default arguments to function parameters. Semantics
>> are like C++." but that's it.
> [...]
> 
> Is this correct behavior? If it is it needs to be documeted better.
> 
> <code>
> import std.stdio;
> 
> class C        { int foo(int x = 1){return x;} }
> class D:C    { int foo(int x = 2){return x;} }
> 
> 
> void main()
> {
>     C c = new D;
>     D d = cast(D)c;
> 
>     writef(c.foo,\n);    // prints 1 (?)
>     writef(d.foo,\n);    // prints 2
> }
> </code>

Hum, quite a catch you got there! Not only should it be better documented, but maybe it shouldn't work this way.
Perhaps the default values should be set not at the call site, but by the function itself (thus depending on it's run-time type, and not it's compile-time type), or maybe it could be as Oskar's idea ("if D inherited default values and forbade them to be redefined in derived classes")

-- 
Bruno Medeiros - CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
« First   ‹ Prev
1 2