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

abstract types lose their prefix #6161

Closed
scabug opened this issue Jul 30, 2012 · 16 comments
Closed

abstract types lose their prefix #6161

scabug opened this issue Jul 30, 2012 · 16 comments

Comments

@scabug
Copy link

@scabug scabug commented Jul 30, 2012

If I change the definition of encode in Names to def encode: Name with ThisNameType then this compiles, which should make it definitive that this is a bug given that ThisNameType <: Name.

trait Namers {
  val global: scala.tools.nsc.Global
  import global.{ Name, Symbol }

  // Relevant definitions in Names
  //
  // def encode: ThisNameType
  // type ThisNameType >: Null <: Name
  //
  def f(x: Symbol): Name = x.name.encode

  /**
  b.scala:5: error: type mismatch;
   found   : Name.this.ThisNameType
   required: Namers.this.global.Name
    def f(x: Symbol): Name = x.name.encode
                                    ^
  one error found
  **/
}
@scabug
Copy link
Author

@scabug scabug commented Jul 30, 2012

@scabug
Copy link
Author

@scabug scabug commented Aug 1, 2012

@retronym said (edited on Aug 1, 2012 8:44:41 AM UTC):
I'm trying to avoid super-cakes at the moment, so I baked a cup-cake version:

object t6161 {
  trait N {
    type Name
  }

  trait N1 extends N {
    class Name {
      type ThisNameType <: Name
      def encode: ThisNameType = ???
    }
  }

  trait S {
    self: N => // change to N1 and it compiles
    type NameType <: Name
  }

  object g extends S with N1

  val n1: g.NameType = ???
  val n2: g.Name = n1.encode
}

This fails uniformly in all versions of Scala that I have at hand.

@scabug
Copy link
Author

@scabug scabug commented Aug 1, 2012

@paulp said:
Nice cupcake. It also compiles if S declares type NameType = Name instead, so it's strictly an issue with upper bounds.

@scabug
Copy link
Author

@scabug scabug commented Jan 30, 2013

@retronym said:
A diff captured with -Ytyper-debug and :t:

https://gist.github.com/4674756

Presented as-is and free of conclusions.

@scabug
Copy link
Author

@scabug scabug commented Jan 30, 2013

@retronym said:
A trace of the asSeenFrom-s

[log typer]  `=> Name.this.ThisNameType`.asSeenFrom(g.NameType, class Name)
[log typer]      `scala.this.Any`.asSeenFrom(<refinement>.type, trait N)
[log typer]      `scala.this.Any`.asSeenFrom(<refinement>.type, trait N) = scala.this.Any
[log typer]      `scala.this.Any`.asSeenFrom(scala.type, package scala)
[log typer]      `scala.this.Any`.asSeenFrom(scala.type, package scala) = scala.this.Any
[log typer]      ` <: S.this.Name`.asSeenFrom(g.type, trait S)
[log typer]          `S`.asSeenFrom(type, module class $iw)
[log typer]          `S`.asSeenFrom(type, module class $iw) = S
[log typer]      ` <: S.this.Name`.asSeenFrom(g.type, trait S) =  <: g.Name
[log typer]      `S`.asSeenFrom(type, module class $iw)
[log typer]      `S`.asSeenFrom(type, module class $iw) = S
[log typer]      ` <: g.Name`.asSeenFrom(g.type, trait S)
[log typer]      ` <: g.Name`.asSeenFrom(g.type, trait S) =  <: g.Name
[log typer]      `N1`.asSeenFrom(type, module class $iw)
[log typer]      `N1`.asSeenFrom(type, module class $iw) = N1
[log typer]  `=> Name.this.ThisNameType`.asSeenFrom(g.NameType, class Name) = => Name.this.ThisNameType

@scabug
Copy link
Author

@scabug scabug commented Jan 31, 2013

@paulp said:
Abstract type NameType with upper bound Name not being recognized. Connect the dots to #7051.

Almost ANY place we call dealiasWiden, it is possible that if we followed this third avenue of progress ("is this an abstract type with an upper bound?") we would discover the type which makes the condition true. This leads to the increasingly ridiculous

def dealiasWidenUpperBoundChain: List[Type]

Over to you...

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@retronym said (edited on Apr 28, 2013 7:23:09 AM UTC):
Here's a seemingly related problem reported on the mailing list (https://groups.google.com/forum/?fromgroups=#!topic/scala-user/1VYTLcxmL8g):

  trait A {
    type Element<:Elt
    trait Elt {
      def doIt(x:Int):Int
      def child(e:Element):Unit
    }
  }
  class A1 extends A {
    class Element extends Elt {
      def doIt(x:Int):Int = x+3
      def child(e:Element):Unit = ()
    }
  }
  class A2 extends A1 {
    class Element extends super.Element {
      override def doIt(x:Int):Int = super.doIt(x)+1
      override def child(e:Element):Unit = super.child(e)
    }
  }
In class A2, method child, the call to super fails with "ambiguous reference to overloaded definition".

I do not understand why:
- super.Element is the direct descendant from the concrete class A1#Element,so super.child should refer to its child method.
- anyway, Elt.child is abstract so it should not be considered.

If it proves to be a separate issue, we could spawn a new ticket.

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@paulp said:
I think that example is "correct" because of #7278. "Same-named inner classes which are in no relationship with each other are explicitly allowed in both Java and Scala." The Element in A2 is not the Element in A1. But parameter types must match exactly. So the method declared in A1#Element has a different signature than the one declared in A2#Element.

If it seems unsound, it's because it is.

@scabug
Copy link
Author

@scabug scabug commented Apr 28, 2013

@paulp said:
Note that if I define child in A2 like this:

override def child(e: A2.super.Element):Unit = super.child(e)

Then the signatures match and the override is fine, but we have a different and also true error:

./a.scala:18: error: class Element needs to be abstract, since method child in trait Elt of type (e: A2.this.Element)Unit is not defined
(Note that A.this.Element does not match A2.this.Element)
  class Element extends super.Element {
        ^
one error found

...because the method signature in A2#Element no longer matches the abstract signature defined in A#Elt. Just for fun I will call super.child from THAT method so we can see all the types at play. This compiles.

trait A {
  type Element<:Elt
  trait Elt {
    def doIt(x:Int):Int
    def child(e:Element):Unit
  }
}
class A1 extends A {
  class Element extends Elt {
    def doIt(x:Int):Int = x+3
    def child(e: A1.this.Element):Unit = ()
  }
}
class A2 extends A1 {
  class Element extends super.Element {
    override def doIt(x:Int):Int = super.doIt(x)+1
    override def child(e: A2.super.Element):Unit = super.child(e)
    def child(e: A2.this.Element):Unit = super.child(e: A2.super.Element)
  }
}

@scabug
Copy link
Author

@scabug scabug commented Oct 15, 2013

@gkossakowski said:
Unassigning and rescheduling to M7 as previous deadline was missed.

@scabug
Copy link
Author

@scabug scabug commented Feb 10, 2014

@paulp said:
I am not sure this can be fixed due to language unsoundness described in #7278.

package p {
  trait N { type Name ; def make(): Name }
  trait N1 extends N { class Name ; def make(): Name = new Name }
  trait N2 extends N { class Name }

  object g extends App with N1 with N2 {
    val x = (this: N2).make()
    // java.lang.ClassCastException: p.N1$Name cannot be cast to p.N2$Name
  }
}

Until we're talking about a language where object g doesn't freely discard information about the types of its inherited members, or where the type x.Name doesn't fluctuate depending on the static type of x, there's no way to draw sound inferences based on the way an abstract type is refined in a trait.

@scabug
Copy link
Author

@scabug scabug commented Feb 10, 2014

@adriaanm said:
Wanted to keep it on my radar until the Sunday haze clears, but probably need to push to 2.12, yes.

@scabug
Copy link
Author

@scabug scabug commented Nov 9, 2015

@retronym said:
We probably ought to issue a warning when a overriding type doesn't conform to its overriden type (dotty has tightened up the rules to reject such programs.)

@scabug
Copy link
Author

@scabug scabug commented Nov 9, 2015

@Blaisorblade said:

We probably ought to issue a warning when a overriding type doesn't conform to its overriden type

+1. I'm also deeply confused by the suggestion in this warning:

scala> trait A { type A = Int }; class B extends A { type A = String }
<console>:10: error: overriding type A in trait A, which equals Int;
 type A needs `override' modifier
       trait A { type A = Int }; class B extends A { type A = String }
                                                          ^

Does it ever make sense to override a type alias? I can still understand override type for an abstract type, but not there.

@scabug
Copy link
Author

@scabug scabug commented Nov 9, 2015

@paulp said:
No comment on whether it makes sense or not, but you can see many places the compiler requires 'override type' on type aliases in the compiler source itself. Including the example which led to this ticket.

Removing an override:

% ./build/pack/bin/scalac src/reflect/scala/reflect/internal/StdNames.scala
src/reflect/scala/reflect/internal/StdNames.scala:228: error: overriding type NameType in trait TypeNamesApi, which equals StdNames.this.TypeName;
 type NameType needs `override' modifier
    type NameType = TypeName
         ^
one error found

@scabug
Copy link
Author

@scabug scabug commented Jul 26, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants