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

by-name argument creates spurious type mismatch #5886

Closed
scabug opened this issue Jun 5, 2012 · 11 comments
Closed

by-name argument creates spurious type mismatch #5886

scabug opened this issue Jun 5, 2012 · 11 comments

Comments

@scabug
Copy link

@scabug scabug commented Jun 5, 2012

object A {
  def f0[T](x: T): T = x
  def f1[T](x: => T): T = x
  def f2[T](x: () => T): T = x()

  val x0 = f0(this.getClass)  // ok
  val x1 = f1(this.getClass)
  val x2 = f2(this.getClass)  // ok

  // a.scala:7: error: type mismatch;
  //  found   : Class[_ <: A.type]
  //  required: Class[?0(in value x1)] where type ?0(in value x1) <: A.type
  // Note: A.type >: ?0, but Java-defined class Class is invariant in type T.
  // You may wish to investigate a wildcard type such as `_ >: ?0`. (SLS 3.2.10)
  //   val x1 = f1(this.getClass)
  //                    ^
  // one error found
}
@scabug
Copy link
Author

@scabug scabug commented Jun 5, 2012

@scabug
Copy link
Author

@scabug scabug commented Jul 16, 2012

Bruno Bieth (mustaghattack) said:
Breaks Scala Specs as Expectable have by-name argument :

"".getClass must haveClass[String]

Doesn't compile anymore

@scabug
Copy link
Author

@scabug scabug commented Nov 16, 2012

@adriaanm said:
bumped the priority because this comes up a lot

@scabug
Copy link
Author

@scabug scabug commented Jan 13, 2013

@retronym said:
The difference between f0 and f1 boils down to the checkPackedConforms check in SuperAccessors, dating all the way back to the introduction of existentials.

scala/scala@8414eba#L10R56

There doesn't appear to be any material difference in Typers itself; both variations lead to an existential skolem ?0 <: A in the type of .getClass.

101,103c113,115
<                 typed A.this.f0: [T](x: T)T
<                 adapted A.this.f0: [T](x: T)T to ?, undetparams=type T
<                 typing this.getClass: pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode BYVALmode POLYmode, silent=false, context.owner=value <local A>
---
>                 typed A.this.f0: [T](x: => T)T
>                 adapted A.this.f0: [T](x: => T)T to ?, undetparams=type T
>                 typing this.getClass: pt = ?: undetparams=, implicitsEnabled=true, enrichmentEnabled=true, mode=EXPRmode POLYmode, silent=false, context.owner=value <local A>
113c125
< [infer method] solving for T in (x: T)T based on (Class[?0])T (solved: T=Class[?0])
---
> [infer method] solving for T in (x: => T)T based on (Class[?0])T (solved: T=Class[?0])
122c134

@scabug
Copy link
Author

@scabug scabug commented Jan 14, 2013

@retronym said:
Nothing breaks with the check disabled.

retronym/scala@scala:2.10.x...retronym:ticket/5886

What was it trying to prevent?

@scabug
Copy link
Author

@scabug scabug commented Jan 14, 2013

@paulp said (edited on Jan 14, 2013 5:00:40 PM UTC):
It seems to be trying to verify that it still typechecks with fresh existentials. That is, if we call f[Set[_ <: AnyRef]], then T=Set[$1] forSome { type $1 <: AnyRef }. Now even if our return type is Set[_ <: AnyRef], it is Set[$2] forSome { type $2 <: AnyRef }, which is no longer T.

It seems relevant that "x: T" is stable but "x: => T" is not, as I discovered when I tried to return x.type from f1.

I'm not 100% sure why "x: () => T" is more acceptable (I'm not 100% sure of the rest of this either, or much of anything else really) but it makes sense on some level which I won't try to articulate.

But in the end, either we can come up with a way that allowing f1 to work is unsound, or we should make it work. These handwavy attempts to retrofit logic onto ancient code are unacceptable.

@scabug
Copy link
Author

@scabug scabug commented Jan 14, 2013

@paulp said:
I would look at something like

def fn(arr1: Array[_ <: AnyRef], arr2: Array[_ <: AnyRef]) 

And see if you can somehow exploit the absence of that check to assign between those arrays.

@scabug
Copy link
Author

@scabug scabug commented Jan 28, 2013

@adriaanm said:
i don't know why this is, but I have a feeling I'd need to reread the TAPL chapter before making a call here
hence, rescheduling

@scabug
Copy link
Author

@scabug scabug commented Jan 28, 2013

@retronym said:
Martin drew a blank when looking at the code and the commit that introduced it.

@scabug
Copy link
Author

@scabug scabug commented May 15, 2013

@scabug
Copy link
Author

@scabug scabug commented May 17, 2013

@paulp said:
01716c8

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