ooc-lang / rock

:ocean: self-hosted ooc compiler that generates c99
http://ooc-lang.org/
MIT License
401 stars 40 forks source link

Weird behaviour with setters calling associated getter #1002

Open marcusnaslund opened 8 years ago

marcusnaslund commented 8 years ago

Example 1 (associated getter):

Test: class {
    value: Int {
        get { return 2 }
        set (x) { 
            if (this value == 2)
                "two" println()
            else
                "not two, but: %i" printfln(this value)
         }
    }
    init: func
}

x := Test new()
x value = 10

should print "two" but prints "not two, but: 0".

Example 2 (setter calling itself):

Same here. But:

Test: class {
    value: Int {
        get { return 2 }
        set (x) {
            "old value=%i" printfln(this value)
            this value = x
        }
    }
    init: func
}

x := Test new()
x value = 2
x value = 3
x value = 4

should always print "old value=2" but prints

old value=0
old value=2
old value=3
horasal commented 8 years ago

It seems that this value in getter is not unwrapped to the getter call.

void test2__Test___setvalue___impl(test2__Test* this, lang_Numbers__Int x) {
    if (this->value == 2) {
        lang_String__String_println(__test2_strLit2);
    }

From source/rock

                // We are in a setter/getter and we're having a variable access. That means
                // the property is not virtual.
                ref as PropertyDecl setVirtual(false)
alexnask commented 8 years ago

Yes, I'm pretty sure this is expected behavior (accessing the property inside the property decl returns the actual backing variable).

marcusnaslund commented 8 years ago

this is expected behavior

But, why? I see no logic in reading the "backing variable" in the setter when, as I have written the code, I would not expect any variable called value at all. I would expect value to always give me 2.

Also, I would expect this code to give the same result as example 1, but it does not:

Test: class {
    value: Int {
        get { return 2 }
        set (x) {
            if (this getTheGetter() == 2)
                "two" println()
            else
                "not two, but: %i" printfln(this value)
         }
    }
    init: func
    getTheGetter: func -> Int {
        this value
    }
}

x := Test new()
x value = 10

That code, in fact, gives me "two".

Is there some logic to this that I'm missing? :)

alexnask commented 8 years ago

This code results in fetching two because of the indirection.

I do think setters could use the getter function when just accessing the property instead of the backing variable, as you've brought out valid points.

I'm trying to think of some weird edgecases that would break but I don't think there are any.
By the way, are you aware of how C# does that?
I'm not quite sure myself as I've never used it but am aware it provides almost the exact same feature.

EDIT: Btw, "expected behavior" was referring to the way rock currently handles this case, not the most intuitive way it could be done.

marcusnaslund commented 8 years ago

are you aware of how C# does that?

The following code

public class Test
{
    public int X
    {
        get { return 2; }
        set
        {
            System.Console.WriteLine("old value={0}", this.X);
        }
    }
}

public class Hello
{
    public static void Main()
    {
        Test test = new Test();
        test.X = 3;
        System.Console.WriteLine("X={0}", test.X);
    }
}

prints

old value=2
X=2

as expected. (In C# I cannot assign this.X = value; in the setter, because this causes an infinite loop.)

vendethiel commented 8 years ago

I'd argue it's a bit different still, because backing properties are explicit in (non-bleeding-edge versions of) c#.

alexnask commented 8 years ago

Ah, I though non-virtual/backing properties were automatically inferred in C# as well, guess I was wrong :)