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
Don't reject views with result types which are TypeVars #7295
Conversation
On the matchesPtInst fast path views are pruned without first being applied. This can result in a false negative in HasMethodMatching if the view has a result type which is a not fully instantiated TypeVar. The fix is to fall back to the slow path in that case. Fixes scala/bug#11174.
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.
Nice minimization, now let's generalize what could possibly go wrong a bit :-)
|
||
if(isView || isViewLike) { | ||
tpInstantiated match { | ||
case MethodType(_, tv: TypeVar) if !tv.instValid => |
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 this check could be more general. What if it's a conversion like def foo(x: From): x.To
? The result type would also be undetermined, right? Or it could be an intersection type of a type variable with some other type.
Could you add a few test cases along these lines and make sure we catch those as well?
Implicits to satisfy views are matched against a search template. To match correctly against the template, TypeVars in the candidates type are replaced by their upper bounds once those bounds have been solved as far as possible against the template.
Trying to get to the bottom of this ... The issue boils down to the way that views are searched for when we're trying to adapt to a member. What we search for is a function from the adaptee to a synthetic refinement of a wildcard When we call The fix in my first commit skirts around this problem by bailing to the slow path straight away. The new fix continues with the fast path, but replaces any uninstantiated I understand your concern about views of other forms, but I think that this really is an interaction between the mechanism for matching against view templates in the presence of type variables. I've added a test case along the lines you suggested anyway ... it passes, but it's not really relevant to the problem. |
Addressed feedback/taking a different approach
} | ||
|
||
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(matchesPtInstCalls) | ||
info.tpe match { |
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.
The indentation looks off here (almost looks like this block is dependant on if StatisticsStatics.areSomeColdStatsEnabled
)
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.
Yes there is. I'll deal with that in a separate commit.
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 like the direction, but I'm not sure we're there yet. How much is there to gain in pruning polymorphic views? Perhaps we can just keep it simple and take the slow path for them?
@@ -571,14 +571,36 @@ trait Implicits { | |||
case _ => false | |||
} | |||
|
|||
object tvarToHiBoundMap extends TypeMap { |
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.
Approximating a type variable by its upper bound is only sound when it occurs in a covariant position, right? See ExistentialExtrapolation
for an analogous type map.
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.
(Also, wildcardExtrapolation
)
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.
That occurred to me too, but see also dominates
: https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/typechecker/Implicits.scala#L455-L456.
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.
Yeah, that's not strictly right either, but has less impact since it's not about conformance.
I'm not sure. It won't make much difference in "large induction" type cases, but it might have an impact where lots of syntax is involved (ie. extension methods with polymorphic carriers). |
So, this is intriguing :-) I ran this simplified example through the debugger, and it looks like an issue with type constructor variables
|
Concretely, there's no case in |
Yup, that's what I was trying to say earlier ;-)
But I think we shouldn't be trying to do this in the first place, hence substituting with the bound. How about if I make the replacement respect variance? |
Apologies, I missed that in your comment. |
I do think that missing case is a bug. We should allow a type constructor var that's applied to type arguments (thus of kind
and patch this in here: |
If you try with |
Why does it not make sense as a bound? |
Also, the other case[*] is routinely comparing type vars against other types, so I'm not sure I follow the other point. [*] |
Well, maybe it does. But I'm slightly puzzled that |
The syntax is confusing, but it just means there's a member named |
The refinement type is created by |
Btw, there's a very similar test already in our suite as test/files/pos/t7785.scala |
All this aside, it's most likely too risky to change type inference to fix this. My curiosity just got piqued... |
Agreed for 2.12.x. But what about for 2.13.x? |
How do you want to proceed from here? Fix the variance issue with the "replace by bounds" version? |
I don't know -- it's tricky :-) I'm worried about regressions, but also concerned with the asymmetry between views and regular implicit search. The type variable bound bug we both discovered could also bite in a regular implicit search. (EDIT: though I can't think of an example, because it could crucially rely on type inference being partially guided by the source type of the implicit conversion) |
This non-view looks comparable and isn't problematic, object Test {
implicit def mkF[F[_]]: F[Int] = ???
implicitly[Option[Int]]
} |
This uses the existing unification mechanisms. I'm looking for an example that decides that |
Given the complexity of it all, I would be inclined to just bail on this for 2.12.8 and use the slow path for polymorphic views. |
OK, I'll do that (and fix the whitespace bug in a separate commit). |
I've taken views off the fast path, and also tentatively removed the |
May be related to #6573 |
Community build run in progress here. |
The community build is almost green. The two failures are classutil and enumeratum. The classutil failure is due to a runtime test that the Scala release version is 2.12.6. So, unrelated to this PR or implicit pruning. I'm not quite sure what the enumeratum failure is, but it's runtime again and doesn't seem obviously related to this PR or implicit pruning in general. The enumeratum build seems quite flaky and I'm not having a lot of luck getting it to build locally even with vanilla 2.12.7 due to Scala.js version slippage. @SethTisue any thoughts on that? |
The "build history" search box for https://scala-ci.typesafe.com/view/scala-2.12.x/job/scala-2.12.x-integrate-community-build/ reveals two prior occurrences: https://scala-ci.typesafe.com/view/scala-2.12.x/job/scala-2.12.x-integrate-community-build/3061/ and https://scala-ci.typesafe.com/view/scala-2.12.x/job/scala-2.12.x-integrate-community-build/3559/ |
agree w/ your & Adriaan's assessment of the results: it's fine |
@adriaanm is this good to go? It'll need carrying over to 2.13.x ... I'd be happy to look at making the fix in |
It would be good to investigate a better fix, but I'm not sure it'll be safe enough to include in 2.13. I guess it depends on the solution, but 2.13 at this point isn't much more permissive of regressions than 2.12. I don't want to distract from the focus of polish and stability for the collections. |
On the
matchesPtInst
fast path views are pruned without first being applied. This can result in a false negative inHasMethodMatching
if the view has a result type which is a not fully instantiatedTypeVar
. The fix is to fall back to the slow path in that case.Fixes scala/bug#11174.