Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The number of super constructor arguments do not need to match #974

Open
marcusnaslund opened this issue Feb 18, 2016 · 13 comments
Open

The number of super constructor arguments do not need to match #974

marcusnaslund opened this issue Feb 18, 2016 · 13 comments

Comments

@marcusnaslund
Copy link
Contributor

Here is a simple example:

Base: abstract class {
    value: Int
    init: func (=value)
}

Derived: class extends Base {
    init: func (v: Int) {
        super()
    }
}

x := Derived new(3)
x value toString() println()

Even though Base has no zero-argument constructor, this code still compiles and runs just fine. The v passed to Derived constructor magically ends up in value.

@vendethiel
Copy link

Isn't super ruby-like just forwarding its arguments?

@horasal
Copy link
Contributor

horasal commented Feb 18, 2016

The compiler does forward all the arguments. You can find the related lines around rock/middle/FunctionCall.ooc: line 428.
But I don't know what the "expected action" should be.

@davidhesselbom
Copy link
Contributor

What if a zero-argument constructor is added in Base? Should the value still "magically" end up in x value, or will the constructor in Derived have to be modified to call super(v) instead of super() for that to happen?

@horasal
Copy link
Contributor

horasal commented Feb 18, 2016

If change the test code to

Base: abstract class {
    value: Int
    init: func (=value)
    init: func~nodefault ()
}

Derived: class extends Base {
    init: func (v: Int) {
        super()
    }
}

x := Derived new(3)
x value toString() println()

It wil print 0 instead of 3, because init: func has higher matching score than init: func(int).

@davidhesselbom
Copy link
Contributor

Thanks for clearing that up - I don't recall it being possible to call super() unless there was a matching zero-argument function in the base class.

So... modifying Base by adding a zero-argument constructor will make Derived call a different function. If this were my program, I think I'd want to be explicit about which function super refers to by calling super(v) instead of super(), then, instead of relying on the arguments being forwarded and possibly calling the wrong function if the base class changes.

@horasal
Copy link
Contributor

horasal commented Feb 19, 2016

Yes, if you call super(v) explictly, it will finally match init: func(int), and the forwarding will not happen.

However, currently compiler will ignore the suffixes of super, so if we have

Base: abstract class {
    init: func~a(arg1: Int)
    init: func~b(arg2: Int)
}

super~b() may no result a call of init~b as both init~a and init~b have the same score.

@horasal
Copy link
Contributor

horasal commented Feb 19, 2016

The following patch makes super+~suffix available.

diff --git a/source/rock/middle/FunctionCall.ooc b/source/rock/middle/FunctionCall.ooc
index 1049553..4a831b2 100644
--- a/source/rock/middle/FunctionCall.ooc
+++ b/source/rock/middle/FunctionCall.ooc
@@ -417,7 +417,7 @@ FunctionCall: class extends Expression {
                 fDecl := trail get(trail find(FunctionDecl), FunctionDecl)
                 superTypeDecl := fDecl owner getSuperRef()
                 finalScore := 0
-                ref = superTypeDecl getMeta() getFunction(fDecl getName(), null, this, finalScore&)
+                ref = superTypeDecl getMeta() getFunction(fDecl getName(), this getSuffix(), this, finalScore&)
                 if(finalScore == -1) {
                     res wholeAgain(this, "something in our typedecl's functions needs resolving!")
                     return Response OK

@alexnask
Copy link
Collaborator

By the way, I would suggest using the super modifier in the function prototype if you need to call the super function anyways.

If you do need to call it conditionally, using all the arguments would obviously be better form, since the addition of a zero argument version of the function would alter behavior.

@davidhesselbom
Copy link
Contributor

"the super modifier"?

@alexnask
Copy link
Collaborator

Yes, you can do something like:

Foo: class {
    init: func

    doThing: func (x: Int) { "Called Foo doThing with #{x}" println() }
}

Bar: class extends Foo {
    init: super func

    doThing: super func
}

It doesn't allow you to add call though, it's pretty much only useful to inherit constructors, since you are required to do it in some cases.

@vendethiel
Copy link

IIRC the tutorial mentions this as a "hackish workaround"?

@fasterthanlime
Copy link
Collaborator

IIRC the tutorial mentions this as a "hackish workaround"?

I don't remember exactly, but perhaps the issue with the super modifier isn't the spec but its implementation.

@alexnask
Copy link
Collaborator

The implementation looks fine (does what I expect) to me and I don't particularly recall it being considered poor style.
Anyway, the use case is rare, I just think it's more elegant to inherit a constructor that way than just using a super call in the body.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants