ooc-lang / rock

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

Rock fails to match generics if class doesn't have new functions #920

Open horasal opened 9 years ago

horasal commented 9 years ago

This fails:

Foo : abstract class<T>{
    init: func
    acceptThis: func (other: This<T>){
        other acceptOther(this)
    }

    acceptOther: func (other: This<T>){
    }
}

describe("Should match generic class", ||
    bar := Foo <Int> new()
    baz := Foo <Int> new()
    baz acceptThis(bar)
)
rock/mytest.ooc:4:15 error No such function acceptOther(Foo<T>) for `Foo<T>`

        other acceptOther(this)
              ~~~~~~~~~~~      

rock/mytest.ooc:7:5 info Nearest match is `Foo acceptOther(other: Foo<T>)`
..but the arg `this` should be of type `Foo<T>` instead of `Foo<T>`

    acceptOther: func (other: This<T>){
    ~~~~~~~~~~~                        

[FAIL]

This compiles and passes:

Foo : class<T>{
    value: T

    init: func(=value)

    acceptThis: func (other: This<T>){
        other acceptOther(this)
    }

    acceptOther: func (other: This<T>){
        value = other value
    }
}

describe("Should match generic class", ||
    bar := Foo <Int> new(2)
    baz := Foo <Int> new(3)
    baz acceptThis(bar)
    assert(baz value == bar value)
)
horasal commented 9 years ago

Some debug info:

Resolving call other acceptOther(this)
Got tDecl ClassDecl Foo <T>, resolving, meta = ClassDecl FooClass <T>

====> Search other acceptOther(this) in FooClass (which has 4 functions)
  - Got Foo acceptThis(other: Foo<T>)!
  - Got Foo acceptOther(other: Foo<T>)!
  - Got Foo __defaults__!
  - Got static Foo __load__!
Regular arg consumes one.
matchesArg, score is now 256
typeScore for Foo<T> vs Foo<T> == -100000    for call other acceptOther(this) (Foo<T> vs Foo<T>) [0x1159d00 vs 0x1159d00]
Final score = -99487
Considering fDecl Foo acceptOther(other: Foo<T>) for fCall other acceptOther(this), score = -99487

    \o/ Found fDecl for acceptOther, it's Foo acceptOther(other: Foo<T>)
** [refScore = -1, ref = Foo acceptOther(other: Foo<T>)] Got suggestion Foo acceptOther(other: Foo<T>) for other acceptOther(this)
Regular arg consumes one.
matchesArg, score is now 256
typeScore for Foo<T> vs Foo<T> == -100000    for call other acceptOther(this) (Foo<T> vs Foo<T>) [0x1159d00 vs 0x1159d00]
Final score = -99487
** New high score, -99487/Foo acceptOther(other: Foo<T>) wins against -1/Foo acceptOther(other: Foo<T>)

T is resolved and does have the ref Class. However, when calculating score, T (TypeAccess->inner) lose its ref.

davidhesselbom commented 9 years ago

It might not help, but, don't you need an init function in the first example?

horasal commented 9 years ago

@davidhesselbom

It works after adding init... but I believe this is just a coincidence. Because the same error also occured in ooc-kean/collections/Vector.ooc:

collections/Vector.ooc:66:14 error No such function copy(Int, Vector<T>, Int, Int) for `Vector<T>`

        this copy ~to (sourceStart, this, targetStart, capacity)
             ~~~~                                               

collections/Vector.ooc:69:2 info Nearest match is `Vector copy~to(sourceStart: Int, target: Vector<T>, targetStart: Int, capacity: Int = 0)`
..but the arg `this` should be of type `Vector<T>` instead of `Vector<T>`

    copy: func ~to (sourceStart: Int, target: Vector<T>, targetStart: Int, capacity := 0) {
    ~~~~                                                                                   

[FAIL] [287ms, 0ms] collections/VectorTest.ooc (compilation error - exit code: 1)

while Vector does have init functions.

I will search for a better test case....

davidhesselbom commented 9 years ago

Vector is an abstract class, unlike Foo above. Does that matter at all?

horasal commented 9 years ago

You are correct.

I have updated the issue by making class abstract.

refs of typeargs are missing if there doesn't exist a new function. However, even though abstract class may have init functions, no new function is generated at all. (rock/middle/ClassDecl.ooc/line216)

I'm still wondering how the ref of typearg becomes null after it has been suggested with Class.

fasterthanlime commented 9 years ago

I'm still wondering how the ref of typearg becomes null after it has been suggested with Class.

That sounds like something that would be done somewhere in rock "to make sure it's re-resolved again properly". The fix is probably a typeArg clone() call away.

This kind of issue is why I started oc / wanted to make a much cleaner compiler, btw. rock sure may be compiling a lot of things, but it's still a pile of hacks, especially as far as generics are concerned.

davidhesselbom commented 9 years ago

@fasterthanlime I know this isn't strictly on topic, but since you brought up generics...

You know how, when you have, say, an ArrayList<T> and you use its [] operator and it returns the value at the index by memcpying T size bytes, even if T is a cover type (because it doesn't know at compile time what T is), and you have this program that uses lists a lot, and you end up with a program that spends most of its time doing memcpy calls? Is that something you consider a problem and want, plan, or think there's something that can be done to improve? I have no expectations at all, I'm just wondering. Also, no sarcasm, just to be clear! :)

fasterthanlime commented 9 years ago

I have no expectations at all

Now you have the right mindset!

It's definitely a problem, but I don't think it's one that can be solved in rock.

davidhesselbom commented 9 years ago

Alright. I vaguely remember you mentioning something about generics and a thesis of yours where you fixed things that you hadn't had the time to add to rock. Can't seem to find anything about that now, though...

fasterthanlime commented 9 years ago

I vaguely remember [...] specialization

Oh, it's real: https://speakerdeck.com/nddrylliog/generic-specialization-in-ooc and https://github.com/fasterthanlime/rock/commits/specialize

Got me a good grade too, like the original j/ooc compiler :) Let's see where this one goes.

davidhesselbom commented 9 years ago

Ooh, there it is! So what became of it? The presentation made it look like things went well, at least, so why is the problem one you

don't think [is] one that can be solved in rock

?

Again, I have no expectations.

davidhesselbom commented 9 years ago

The reason I'm asking is, well, that application I mentioned where most of the time is spent in memcpy... that's the one I'm making. And while I can imagine ways of implementing lists other than by using ooc generics, I'd prefer not to, so I'd like to know whether specialization is still in its own branch because it won't work after all, or because you simply haven't had the time and neither has anyone else, or because it will break everything ever built in ooc, and not just the SDK, or if there's some other reason that I've missed. If it's just a matter of time constraints, I (and the rest of my team) would be interested in helping out.

fasterthanlime commented 9 years ago

why is specialize unmerged

I mention a few reasons in the deck:

The thing with adding more big things to rock is: I'm not going to do it, and I don't think anybody else should (but I'm not anybody else's parent..). I tried to subtly hint you by linking to cork (here's its original motivation) - a new compiler project of mine.

And I'm pretty sure the reaction of anyone using ooc semi-seriously is "shit, no, Amos has yet ANOTHER pet project on top of music and shit, we'll never get anything fixed in rock now, which is the only semi-useful piece of software he's written" — but please, read the cork doc. Feel my pain. It's a miracle rock ever got to compile what it did, because had we (the authors) been slightly less stubborn, we'd have realized how broken it was years ago.

The thing is, we did realize how broken it was, to some extent. oc was started in 2010, that's just one year after rock. But then I was so busy fixing up rock (mostly because people bitched at me "hey come fix bugs on the real thing instead of having fun on vaporware" — but also because, well, I was using rock to write oc) that I didn't get too far. And it was too ambitious, it was a "fuck everything we've done until now, let's do all the outrageous things" (pluggable backends, coroutines, woo).

The worst thing about rock is not that some features work only halfway, it's not the random crashes, it's not the unreliable maintainer (me) that sometimes uploads broken bootstraps, the worst thing about rock is that complexity within the codebase isn't separated. It could be a complex piece of software that works in various passes, cleanly separated, but it isn't.

To prove my point, let me open VariableAccess.ooc, (should be a simple AST node, right?) and list the ways.. I mean the responsibilities this class has:

And I've omitted thing for the sake of everyone's sanity. Not trying to shut anyone up, but, c'mon, I've been dealing with that for 6 years, don't I get a third chance now?

davidhesselbom commented 9 years ago

Dude, relax!

I tried to subtly hint you by linking to cork

... and I didn't even look long enough to see that it said "Another attempt at an ooc compiler.", but just assumed it was another language project like Pug. I feel horrible now. Sorry!

But implementing performant immutable data structure is another big project, and since my time is limited, I believe a better compiler can be built.

I don't see how that can possibly be a faster solution, but you would know better than me! If you think you can make a better compiler than rock faster by starting over from scratch, I wholeheartedly support you in doing so. I might even have a shot at a chance of understanding how it works by seeing it evolve.

fasterthanlime commented 9 years ago

Dude, relax!

Haha :) I wasn't angry at all when I wrote that! Just explaining, it's worth it to me too, I have to re-understand all of rock in order to write another piece of software that does the same thing (but differently).

davidhesselbom commented 9 years ago

Assuming you intend to make cork (ooooh, I see it now) completely compatible with existing ooc code, you have a nice suite of tests now that you didn't have when you made rock. That should help :)

fasterthanlime commented 9 years ago

That should help :)

My feeling too. I'm curious if I can get stuff like that working:

do: func (f: Func <T> (Int, Int) -> T) {
    f(1, 2)
}

do(|x, y|
    x + y
)

do(|x, y|
    "#{x}#{y}"
)

(It fails miserably in rock, if you were wondering...)

vendethiel commented 9 years ago

I'm happy to see cork :D. That example would be sweet, what is the reason it fails in rock? Matching of types?

davidhesselbom commented 9 years ago

Shouldn't it be

do: func (f: Func <T> (Int, Int) -> T) -> T {
    f(1, 2)
}

?

fasterthanlime commented 9 years ago

@davidhesselbom Doesn't matter, I'm not using the return value of f - rock fails beforehand.

vendethiel commented 9 years ago

is the still available for the outer function's return type?

fasterthanlime commented 9 years ago

@vendethiel only if it had <T> itself, otherwise it would not be a parameterized function and T would simply be an undefined symbol.