Issue an error when trying to use a method as a qualifier.#6042
Issue an error when trying to use a method as a qualifier.#6042hrhino wants to merge 2 commits intoscala:2.12.xfrom
Conversation
The check for `implicitsEnabled` here seems older even than that method; it appears to originally have looked at a flag `reportGeneralErrors`, which seems a reasonable flag if you want to check whether to report an error. An erroneous tree that we do not error on, however, can be use as a qualifier which will not necessarily cause an error until past typer, when something finally notices the mistake and blows up. Fixes scala/bug#10474.
| @@ -894,7 +894,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper | |||
|
|
|||
| def cantAdapt = | |||
| if (context.implicitsEnabled) MissingArgsForMethodTpeError(tree, meth) | |||
There was a problem hiding this comment.
I agree this condition makes no sense. Whether implicit conversions are enabled or not has no bearing on eta expansion.
EDIT: it looks like it's checking, in a pretty roundabout way, that we're not inside a pattern:
| | | |-- fuzzy_df1.Fuzz : pt=Int PATTERNmode (site: value x$1 in A) enrichment only
| | | | |-- `fuzzy_df1` EXPRmode-POLYmode-QUALmode (site: value x$1 in A) enrichment only
"enrichment only" is our way of saying full on implicit search is disabled (conversions still allowed)
There was a problem hiding this comment.
def cantAdapt =
if (mode.inQualMode) UnstableTreeError(tree)
else MissingArgsForMethodTpeError(tree, meth)
seems like the way to go EDIT: not
There was a problem hiding this comment.
It is kind of suspicious we are not getting an error from stabilize, but this seems like a move in the right direction. Maybe leave that as a TODO?
There was a problem hiding this comment.
ok, so pulling on that thread some more -- pardon the stream of consciousness review -- but it seems like we need to keep pulling.
What should the error be in the following snippet?
class Fuzzy { val Fuzz = 1 }
def fuzzy_df1(x: Int): Fuzzy = new Fuzzy
var z = fuzzy_df1.Fuzz
I say it should be the current one:
missing argument list for method fuzzy_df1
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `fuzzy_df1 _` or `fuzzy_df1(_)` instead of `fuzzy_df1`.
stability is not the issue here. It's also weird to complain that Fuzz is not a member of the method type (x: Int): Fuzzy since methods are not values and thus cannot have members (that's why they are eta-expanded to functions, which are values.)
There was a problem hiding this comment.
That error sounds reasonable. My patch doesn't change it in that case. Perhaps cantAdapt should always be MissingArgsForMethodTypeError, after all.
It is kind of suspicious we are not getting an error from
stabilize, but this seems like a move in the right direction.
The instability errors that I based my guess on are from stabilize, but qualifiers of patterns aren't typed in PATTERNmode so it doesn't complain there.
Putting on my end-user hat, I see:
scala> (??? : Any) match { case Foo.Bar.Baz => }
<console>:13: error: missing argument list for method Bar in object Foo
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `Bar _` or `Bar(_)` instead of `Bar`.
and I'm not sure how helpful of an error message this is:
scala> (??? : Any) match { case Foo.Bar.Baz _ => }
<console>:14: error: '=>' expected but '_' found.
(??? : Any) match { case Foo.Bar.Baz _ => }
^
scala> (??? : Any) match { case Foo.Bar.Baz(_) => }
<console>:15: error: stable identifier required, but Foo.Bar found.
(??? : Any) match { case Foo.Bar.Baz(_) => }
^
The latter one was also a crasher before this patch.
The "missing argument list for method Bar in object Foo" is reasonable; perhaps MissingArgsForMethodTpeError could give better advice if we get to cantAdapt in PATTERNmode? I'm rightly not allowed to write user-facing messages at my job, but something like "Unapplied methods cannot be used as patterns. If you meant to define an extractor, Foo should be a value with an unapply or unapplySeq method."
There was a problem hiding this comment.
qualifiers of patterns aren't typed in PATTERNmode so it doesn't complain there.
Spot on, I think that points in the direction of a better error message. Maybe we should track that the context is typing a pattern (looks like we have isTyperInPattern, which would be better implemented as a context mode, similar to PatternAlternative)? In fact, context.implicitsEnabled seems to be doing just that, albeit in a pretty roundabout way (we type patterns with enrichment enabled, but general implicits turned off)
Perhaps cantAdapt should always be MissingArgsForMethodTypeError, after all.
I tried that as well, but it looks like we need something more subtle.
I'll try to sit down later this week and add some more concrete suggestions.
There was a problem hiding this comment.
Sheesh. The hole just keeps on going.
scala> def Foo(x: Int) = x
Foo: (x: Int)Int
scala> object Bar { def Bip(x: Int) = x }
defined object Bar
scala> (??? : Any) match { case Foo => }
<console>:13: error: stable identifier required, but Foo found.
(??? : Any) match { case Foo => }
^
scala> (??? : Any) match { case Foo(_) => }
<console>:13: error: not found: value Foo
(??? : Any) match { case Foo(_) => }
^
scala> (??? : Any) match { case Bar.Bip(_) => }
<console>:13: error: method Bip is not a case class, nor does it have an unapply/unapplySeq member
(??? : Any) match { case Bar.Bip(_) => }
^
scala> (??? : Any) match { case Bar.Bip => }
<console>:13: error: stable identifier required, but Bar.Bip found.
(??? : Any) match { case Bar.Bip => }
^
It's a nice little tour of all sorts of typer errors. I'm kind of leaning, now that I think about it, in the direction of the CaseClassConstructorError we get in the Bar.Bip case, but it would be kinda cool if the same one showed up for the Foo(_) case (I can't see why they're different, from an error perspective). The third error at least points in the direction of what's wrong;
The instability one gets less and less justifiable the more I look at it.
Maybe just:
scala> (??? : Any) match { case Bar.Bip => }
<console>:13: error: method `Bar.Bip` cannot be used as an identifier pattern;
identifier patterns must refer to values.
(??? : Any) match { case Bar.Bip => }
^
scala> (??? : Any) match { case Bar.Bip(_) => }
<console>:13: error: method Bar.Bip cannot be used as an extractor pattern;
perhaps you meant to define a value `Bip` with an `unapply` or `unapplySeq` method?
(??? : Any) match { case Bar.Bip(_) => }
^
There was a problem hiding this comment.
Nice exploration :-) Agreed on stability -- not really the best thing to complain about here. Oh scalac.
There was a problem hiding this comment.
For (??? : Any) match { case Foo => }, the fix could also be turning Foo into a val.
There was a problem hiding this comment.
I can't think of a reason why case Foo(_) and case Bar.Bip(_) should result in different errors. I like your suggested rewordings for both errors.
|
|
||
| def bingo(l: Int, r: Int) = l + r | ||
|
|
||
| val `bingo`.Fuzz = 1 |
There was a problem hiding this comment.
Great job, I wanted to find some time to fix this issue. Currently, def F(k:Int)=k;val F.K = () throws a NullPointerException.
|
Let's give ourselves some more time to refine this. |
The two new modes are for now for diagnostics only; they replace a stray boolean flag isTyperInPattern. The error messages are up for debate, but I think most of them are justifiable. See ticket 10474.
|
Updated in a stream-of-consciousness coding spree inspired by Adriaan's stream-of-consciousness review. I didn't amend so that the diff of the recent changes can be diffed against the diff of the older changes, in case anyone cares. I'll squash after it gets an LGTM. |
|
I think I want to re-think this. I'll re-open with the re-thought. Sorry for putting this off. |
The check for
implicitsEnabledhere seems older even than that method; it appears to originally have looked at a flagreportGeneralErrors, which seems a reasonable flag if you want to check whether to report an error. An erroneous tree that we do not error on, however, can be use as a qualifier which will not necessarily cause an error until past typer, when something finally notices the mistake and blows up.I tried, but I can't find how we can get to
cantAdaptwith disabled implicits other than by the tricks shown in the test case; running the partests with an assertion that atcantAdaptwe either haveImplicitsEnabledor are inQUALmodenever trips. The main un-setters ofImplicitsEnabledaretypedSingletonTypeTreeandtypedPattern, which are the two at issue in this PR.I'm not completely sure about the use of
UnstableTreeErroras the error; originally I had aCannotSelectFromMethodErrorbut abandoned it in light of:Moreover, the relevant spec sections regarding pattern matching and singleton types at least imply that instability is part of the problem here. I'm willing to add a new error message if this seems imprecise.
Fixes scala/bug#10474.