-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Align specificity of (a: T)... vs (a: T*)... with mono case
#7680
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
Conversation
|
Or maybe we shouldn’t be instantiating type vars to a non-expression type like a repeated param type? |
This comment has been minimized.
This comment has been minimized.
033039b to
8adcadb
Compare
|
|
Humm, lots of test failures. I'm deferring this to 2.13.1 -- there's no need to get this into 2.13.0 |
|
I forgot this is affecting specs, moving back to RC1. |
|
Abstractly, this is because of auto-tupling? I remember Lukas mentions the implementation on one of the old tickets, maybe not these.
|
1c235d6 to
fd9dcb2
Compare
This comment has been minimized.
This comment has been minimized.
|
Thanks! Auto-tupling was also hiding the second class T*, pretending like we can make values of that. Added a few bugs to the fixlist |
fd9dcb2 to
4cfeab8
Compare
|
🐰🕳 so, i see no other way out but to adapt an |
|
To coin a phrase, when I was learning scala the handling of |
|
Urban dictionary doesn't help me this time with "to coin a phrase". |
4cfeab8 to
41cd12e
Compare
... through drastic means: T* now only conforms (isCompatible) to
other repeated parameter types. This breaks case classes with
repeated param fields unless we add a case to adapt....
The following type checked (choosing the single-param overload):
```
def foo(xs: Int*): Int = xs.toSeq.head
def foo(x: Int): Int = x
foo(2)
```
Whereas this was considered ambiguous:
```
def fooT[T](xs: T*): T = xs.toSeq.head
def fooT[T](x: T): T = x
fooT(2)
```
Another instance of the mono and poly cases of isApplicableToMethod
being out of whack.
`isApplicableToMethod` will use formalTypes to eliminate
the repeated param in the formal types at the start,
but we keep the repeated marker in the arguments -- here's a debug log:
/*
inferMethodAlternative all: List(method foo, method foo)
isCompatibleArgs false (List(Int*), List(Int))
isAsSpecific false: (xs: Int*)Int >> (x: Int)Int?
--> the repeated case is not more specific than the single-arg case because
you can't apply something of `Int*` to `Int`
isCompatibleArgs true (List(Int), List(Int))
isAsSpecific true: (x: Int)Int >> (xs: Int*)Int?
--> the single param case is more specific than the repeated param case, because
you can apply a single argument to the method with the repeated param
isCompatibleArgs true (List(Int), List(Int))
isAsSpecific true: (x: Int)Int >> (xs: Int*)Int?
isCompatibleArgs false (List(Int*), List(Int))
isAsSpecific false: (xs: Int*)Int >> (x: Int)Int?
isCompatibleArgs true (List(Int), List(Int))
isAsSpecific true: (x: Int)Int >> (xs: Int*)Int?
isCompatibleArgs false (List(Int*), List(Int))
isAsSpecific false: (xs: Int*)Int >> (x: Int)Int?
inferMethodAlternative applicable List(method foo, method foo) --> ranked: List(method foo)
*/
The bug in the polymorphic case of `isApplicableToMethod`
was that `adjustTypeArgs` would replace the `T*` by `Seq[T]`,
thus removing the incompatibility in applying a `T*` when
a `T` is expected. Sadly, the bounds check also didn't catch it,
since `T* <:< Any`...
There was a similar issue in that we would tuple a T* with other args
while trying to deal with arity mismatch. Also exclude T* there.
41cd12e to
1a2cc1b
Compare
|
🎉 |
|
Now I'll have to go back and change all my stack overflow answers. If you're a bad mono case you should stay home and recover. |
|
This one definitely had me stereo-the-window. |
| - A parameterized method $m$ of type `($p_1:T_1, \ldots , p_n:T_n$)$U$` is | ||
| _as specific as_ some other member $m'$ of type $S$ if $m'$ is [applicable](#function-applications) | ||
| to arguments `($p_1 , \ldots , p_n$)` of types $T_1 , \ldots , T_n$. | ||
| to arguments `($p_1 , \ldots , p_n$)` of types $T_1 , \ldots , Tlast$; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be Tn rather than Tlast ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tlast is the type of the last arg in the m-prime application; it may be taken as T (instead of T*) or just Tn.
| def isCompatibleArgs(tps: List[Type], pts: List[Type]) = { | ||
| val res = (tps corresponds pts)(isCompatible) | ||
| // println(s"isCompatibleArgs $res : $tps <:< $pts") | ||
| res |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you need an @inline printing[A](msg, a: => A): A thingie with elidable printing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How I learned to stop worrying about performance and have debug output when I need it. Comments! Higher-tech would be nice, but it would also look like regular code, when it isn't really.
|
|
||
| val tp1 = methodToExpressionTp(tp) | ||
| // can only compare if both types are repeated or neither is (T* is not actually a first-class type, even though it has a BTS and thus participates in subtyping) | ||
| (!isRepeatedParamType(tp) || isRepeatedParamType(pt)) && { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll proceed through this if we have a Java and Scala repeated param type. Array[T] <=> Seq[T] adaptations might then come into play below. I haven't thought this through enough to know if this matters, but did you consider more fine grained checking ("are both/neither java repeated?" "are both/neither Scala repeated").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't give it that much thought, but I think it's ok. This is to shield us from comparing the second-class repeated param types to first-class expression types.
If tp is a repeated param type, so must pt be. If we don't check that, we'll go up the BTS of those types, hitting Seq / Array, and then relating those repeated param types to regular expression types. We should really think of repeated param types as abstract types that you can only convert out of with a conversion (that conversion is already in adapt because of the tighter conformance implemented here.)
| instantiatePossiblyExpectingUnit(tree, mode, pt) | ||
| } | ||
| // TODO: we really shouldn't use T* as a first class types (e.g. for repeated case fields), but we can't allow T* to conform to other types (see isCompatible) because that breaks overload resolution | ||
| else if (isScalaRepeatedParamType(tree.tpe) && !isScalaRepeatedParamType(pt)) adapt(tree setType(repeatedToSeq(tree.tpe)), mode, pt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks okay on the assumption that the * only appears in the outermost part of tree.tp / pt.
For case classes, where we have the unfortunate case of * appearing at all, this seems to hold true:
scala> class C(val c: String*)
defined class C
scala> new C("")
res0: C = C@558756be
scala> res0.c
val c: String*
scala> res0.c
res1: Seq[String] = WrappedArray("")
scala> class C(val c: String*) { def foo = Seq(c) }
defined class C
scala> new C("").foo
def foo: Seq[Seq[String]]
scala> { () => new C("").foo }
res3: () => Seq[Seq[String]] = $$Lambda$1467/1307655632@2ed7978c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, we shouldn't emit fields with type T*, but then we need to adjust other logic to turn those back into varargs where needed. I chose the expedient route :-)
the language in question was added by Adriaan in scala#7680 but then accidentally got removed in 2020 by scala#7432 I looked through other spec commits in that date span and didn't find anything else that went missing
the language in question was added by Adriaan in scala#7680 but then accidentally got removed in 2020 by scala#7432 I looked through other spec commits in that date span and didn't find anything else that went missing
Fix scala/bug#11015
Fix scala/bug#4775
Fix scala/bug#8344
TODO:
Seq[T]would be seen as compatible toTsnippet of interest
The following type checked (choosing the single-param overload):
Whereas this was considered ambiguous:
Another instance of the mono and poly cases of isApplicableToMethod
being out of whack.
isApplicableToMethodwill use formalTypes to eliminatethe repeated param in the formal types at the start,
but we keep the repeated marker in the arguments -- here's a debug log:
The bug in the polymorphic case of
isApplicableToMethodwas that
adjustTypeArgswould replace theT*bySeq[T],thus removing the incompatibility in applying a
Seq[T]whena
Tis expected.