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

lub as calculated by TypeKinds is incorrect #3872

Closed
scabug opened this issue Sep 25, 2010 · 9 comments
Closed

lub as calculated by TypeKinds is incorrect #3872

scabug opened this issue Sep 25, 2010 · 9 comments
Labels

Comments

@scabug
Copy link

scabug commented Sep 25, 2010

Here is another one. The icode checker would crash finding an unexpected type on the stack, and I eventually pinpointed that it is calculating strange lubs. Here is a line from SymbolLoaders:

sym.setInfo(if (ok) NoType else ErrorType);

Both NoType and ErrorType have Type as a superclass, so one would expect the type of that expression to be Type. However when calculating the lub, what it actually calculates is Product.

Pushing: REFERENCE(scala.tools.nsc.symtab.Types$$NoType)
meet2 created new stack: (List(REFERENCE(scala.tools.nsc.symtab.Types$$NoType)),
List(REFERENCE(scala.tools.nsc.symtab.Types$$ErrorType))) =>
List(REFERENCE(scala.Product))

The implementation is merely:

global.lub(t1 :: t2 :: Nil)

Before erasure the lub shows up as Product with Type. If I read the spec correctly it's allowed to not order this with the class first; but if that is indeed allowed, there must be some interface which utilizes intersectionDominator in erasure so the right type pops out, because I can't see when "Product" is ever what you'd want.

A seemingly simpler and more direct answer is to organize compound types with the class first all the time. Maybe there's a reason not to do that.

@scabug
Copy link
Author

scabug commented Sep 25, 2010

Imported From: https://issues.scala-lang.org/browse/SI-3872?orig=1
Reporter: @paulp
Assignee: @magarciaEPFL

@scabug
Copy link
Author

scabug commented Sep 25, 2010

@paulp said:
An illustration of the calculated lub from everyday scala land:

scala> abstract class Tom
defined class Tom

scala> case object Bob extends Tom
defined module Bob

scala> case object Harry extends Tom
defined module Harry

scala> List(Bob, Harry)
res0: List[Product with Tom] = List(Bob, Harry)

@scabug
Copy link
Author

scabug commented Sep 27, 2010

@harrah said:
This looks like it could be a duplicate of #3868.

@scabug
Copy link
Author

scabug commented Sep 27, 2010

@paulp said:
Replying to [comment:3 harrah]:

This looks like it could be a duplicate of #3868.

Funny, it's been doing the lub that way since scala 0.minus-infinity and we come in two days apart in the late 3000s. Yes, it's a duplicate. I know you were dying to close it. Go ahead, it is rightfully yours. I'll just go over here and oh, I don't know, FIX THE LUB!

@scabug
Copy link
Author

scabug commented Sep 28, 2010

@dragos said:
There is no one 'right' lub after erasure, unfortunately. This is one of the reasons we still don't have a jvm6 backend. Add two methods to your example:

  def moo(x: Tom) {}
  def moo2(x: Product) {}
  moo(if (c) new Bob else new Harry)
  moo2(if (c) new Bob else new Harry)

I agree the lub should list classes first, but it won't solve the icode checker issue. The best way would be to simply make the icode checker less precise, and use the JVM notion of subtyping (which basically considers any interface type to be Object).

@scabug
Copy link
Author

scabug commented Sep 28, 2010

@paulp said:
Replying to [comment:7 dragos]:

I agree the lub should list classes first, but it won't solve the icode checker issue. The best way would be to simply make the icode checker less precise, and use the JVM notion of subtyping (which basically considers any interface type to be Object).

This matches my understanding. So to summarize, there are two changes here: compound types list the class first (the classES first I suppose, since it general it offers no complaint about types like classA with classB) and then changing TypeKinds so the lub of an interface and any legal type is REFERENCE(AnyRef). If that sounds about right I am available to make these modifications.

@scabug
Copy link
Author

scabug commented Sep 28, 2010

@paulp said:
(In r23131) Modified typekinds to offer a more general lub when it encounters
interfaces so it does not end up in a disagreement with the jvm.
References #3872, but modifying the compiler lubs is not yet done.
Review by dragos.

@scabug
Copy link
Author

scabug commented Apr 3, 2012

@magarciaEPFL said:
In case the goal was computing lubs as needed to emit 1.6 classfiles (ie version 50 and up), that's been solved in

magarciaEPFL/scala@da9e0eb

Background, quoting from http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf

--- start quote ---
The simplest solution to the [least upper bound of] interface problem is to be found in Sun’s implementation of the
JDK bytecode verifier. (This approach is documented nowhere, but can easily be inferred by
experimentation.) Namely, bytecode verification ignores interfaces, treating all interface types as
the class type Object. Thus, the type algebra used by the verifier contains only proper classes
and no interfaces, and subtyping between proper classes is simply the inheritance relation between
them. Since Java has single inheritance (a class can implement several interfaces, but inherit from
one class only), the subtyping relation is tree-shaped and trivially forms a semi-lattice: the least
upper bound of two classes is simply their closest common ancestor in the inheritance tree.
--- end quote ---

See also http://comments.gmane.org/gmane.comp.java.vm.languages/2293

@scabug
Copy link
Author

scabug commented Jun 20, 2012

@magarciaEPFL said:
I'm closing this ticket as Not-a-bug because its underlying assumption, namely that TypeKind.lub should be used in the jvm6 backend, simply doesn't hold and never will.

Instead TypeKind.lub is fine for the uses it is called for (GenICode, ICodeCheckers, and type-flow analysis). In fact, for those uses, TypeKind.lub should stay as-is.

As an aside, the following excerpt from GenASM shows how lubs are computed the JVM way:

  @inline final private def jvmWiseLUB(a: Symbol, b: Symbol): Symbol = {

    assert(a.isClass)
    assert(b.isClass)

    val res = Pair(a.isInterface, b.isInterface) match {
      case (true, true) =>
        global.lub(List(a.tpe, b.tpe)).typeSymbol // TODO assert == firstCommonSuffix of resp. parents
      case (true, false) =>
        if(b isSubClass a) a else ObjectClass
      case (false, true) =>
        if(a isSubClass b) b else ObjectClass
      case _ =>
        firstCommonSuffix(superClasses(a), superClasses(b))
    }
    assert(res != NoSymbol)
    res
  }

@scabug scabug closed this as completed Jun 20, 2012
@scabug scabug added the backend label Apr 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant