This page is no longer maintained — Please continue to the home page at www.scala-lang.org

the secret diary of bug #1107

5 replies
extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.

In order to fix this bug acceptably I need to form a better intuition on something. I discovered that
inserting a println at a particular point fixed the bug. This eventually led me to discover the epic burn
hidden in here:

val symtpe =
if ((sym hasFlag Flags.MODULE) && (sym.linkedModuleOfClass ne NoSymbol))
singleType(sym.tpe.prefix, sym.linkedModuleOfClass) // e.g. None, Nil
else sym.tpe

The burn is that calling sym.linkedModuleOfClass actually changes its result from something that isn't
NoSymbol to NoSymbol. The conditional above makes sure that sym.linkedModuleOfClass ne NoSymbol, little
realizing that it just made it NoSymbol by checking!

// adding these lines just before the above code...
println("1: sym.linkedModuleOfClass = " + sym.linkedModuleOfClass + " (sym = " + sym + ")")
println("2: sym.linkedModuleOfClass = " + sym.linkedModuleOfClass + " (sym = " + sym + ")")

// tell us this:
1: sym.linkedModuleOfClass = object C$$P (sym = object P)
2: sym.linkedModuleOfClass = (sym = object P)

I sort of have a handle on the lengthy list of conditions which led to this particular manifestation, and
fixing this bug in a specific-to-this-bug way wouldn't be difficult, but what I don't have much of a handle on
is when (by which I mean "which compiler phases?" and "which types?" or anything else that would help narrow
it down) I should expect this sort of kill-the-cat-by-opening-the-box behavior.

odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: the secret diary of bug #1107

I am not sure I understand completely what you are getting at. How
does sym.linkedModuleOfClass change the result? Maybe the underlying
issue is this (but I might be wrong):

When populating the members of a package, the compiler is not allowed
to look inside the classfiles. So all it can do is assume there is for
each classfile a class and a module in that class (which are linked).
Once it has inspected the classfile it knows better. Sometimes there
is no class, and sometimes there is no companion object. The symbol
has already been allocated ad cannot be removed. So in that case the
type of the symbol is set to NoType, to indicate that the symbol is
not really there. Not sure whether this accounts for what you are
seeing.

Cheers

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: the secret diary of bug #1107

On Sun, Feb 15, 2009 at 08:00:16PM +0100, martin odersky wrote:
> I am not sure I understand completely what you are getting at. How
> does sym.linkedModuleOfClass change the result?

It's somewhere in the process of iterating over the declarations:

owner.rawInfo.decl(name.toTermName).suchThat(
sym => (sym hasFlag MODULE) && (sym isCoDefinedWith this))
)

The actual crashing failure happens in rebind, but presumably it shouldn't get this far:

// this is called with sym == NoSymbol so crash
private def rebind(pre: Type, sym: Symbol): Symbol = {
val owner = sym.owner

I will track this down to an exact line, but I can say with confidence that one receives differing answers on
consecutive calls to linkedModuleOfClass.

> Sometimes there is no class, and sometimes there is no companion object. The symbol has already been
> allocated ad cannot be removed. So in that case the type of the symbol is set to NoType, to indicate that
> the symbol is not really there. Not sure whether this accounts for what you are seeing.

If I'm understanding you correctly it does account for it, but also leaves me in about the same position I was
before, in that consecutive identical calls can return different results. If this info discovery process can
be induced by calling a method like linkedModuleOfClass, then I can't really rely on the return value of that
method without calling it twice.

The list of circumstances necessary to reproduce this bug is perhaps illustrative of its peculiar nature, as
according to the submitter (and I've confirmed most of them) changing any of these make it go away:

* Put O in the same file as the traits
* Remove the private modifier from object P
* Pull P out of C
* Remove sealed from Top or Sub
* Make C an object or a class
* Make P a trait or a class

DRMacIver
Joined: 2008-09-02,
User offline. Last seen 42 years 45 weeks ago.
Re: the secret diary of bug #1107
On Sun, Feb 15, 2009 at 7:00 PM, martin odersky <martin [dot] odersky [at] epfl [dot] ch> wrote:
I am not sure I understand completely what you are getting at. How
does sym.linkedModuleOfClass change the result?

Hi Martin,

The behaviour Paul is describing seems a bit strange. I'm not sure if I understand if you're saying whether it's deliberate behaviour or merely a comprehensible bug. If it's the former, would you mind explaining the reasoning as it seems highly counter intuitive?

As I understand it, from a class symbol we can call linkedModuleOfClass to get the.. well, linked module. i.e. the companion object. It would seem to me that this would always result in the same thing, and that when we have to load that information from another source file or from a class file we woul dload it at first invocation and then it would not change. Certainly it seems to be assumed in various places in the code that the results of this are stable.

However, paul seems to be reporting that the results are in fact not stable, and this seems rather worrying to me. It may be that what's actually happening is that we're calling it in an invalid state (e.g. the fact that we're calling linkedModuleOfClass on something we've just verified is a module is not encouraging) and this is what results from doing so, but it would be good to be able to distinguish between intended behaviour and a bug here.

Thanks,
David
odersky
Joined: 2008-07-29,
User offline. Last seen 45 weeks 6 days ago.
Re: the secret diary of bug #1107

On Mon, Feb 16, 2009 at 6:08 PM, David MacIver wrote:
> On Sun, Feb 15, 2009 at 7:00 PM, martin odersky
> wrote:
>>
>> I am not sure I understand completely what you are getting at. How
>> does sym.linkedModuleOfClass change the result?
>
> Hi Martin,
>
> The behaviour Paul is describing seems a bit strange. I'm not sure if I
> understand if you're saying whether it's deliberate behaviour or merely a
> comprehensible bug. If it's the former, would you mind explaining the
> reasoning as it seems highly counter intuitive?
>
> As I understand it, from a class symbol we can call linkedModuleOfClass to
> get the.. well, linked module. i.e. the companion object. It would seem to
> me that this would always result in the same thing, and that when we have to
> load that information from another source file or from a class file we woul
> dload it at first invocation and then it would not change.

I think the results should be stable, except for this problem with
lazy class loading.
There are a number of situations where we can't load the class on
first invocation because it would pull in a huge library graph or
result in a loop. This might be one of them. To find out, we'd need to
get to the bottom of this.

> Certainly it
> seems to be assumed in various places in the code that the results of this
> are stable.
>
> However, paul seems to be reporting that the results are in fact not stable,
> and this seems rather worrying to me. It may be that what's actually
> happening is that we're calling it in an invalid state (e.g. the fact that
> we're calling linkedModuleOfClass on something we've just verified is a
> module is not encouraging) and this is what results from doing so, but it
> would be good to be able to distinguish between intended behaviour and a bug
> here.
>
It does seem that it's called in an invalid state. Btw to make sure
that linkedModuleOfClass is stable, you should call initialize on the
class first.

I.e.

clazz.initialize.linkedModuleOfClass.

initialize will compute the info of the class, which loads the
classfile as a side effect. we can't rolll this into
linkedModuleOfClass, though, because of the problems I described
above.

Cheers

extempore
Joined: 2008-12-17,
User offline. Last seen 35 weeks 3 days ago.
Re: the secret diary of bug #1107

Part of the mystery here stems from my recent discovery that there are at least two separate bugs in the mix.
The following code generates a broken classfile through some interaction of sealed and private, both of which
are required for the broken classfile.

sealed trait Top
trait C {
private object P extends Top
}

% scalac *.scala ; scala -e 'new AnyRef with C'
error: error while loading Sub, class file '/scala/trac/1107/./Sub.class' is broken
(error reading Scala signature of /scala/trac/1107/./Sub.class: malformed Scala signature of Sub at 213; reference value P of trait C refers to nonexisting symbol.)
one error found

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland