Skip to content

Issue an error when trying to use a method as a qualifier.#6042

Closed
hrhino wants to merge 2 commits intoscala:2.12.xfrom
hrhino:bugfix/t10474
Closed

Issue an error when trying to use a method as a qualifier.#6042
hrhino wants to merge 2 commits intoscala:2.12.xfrom
hrhino:bugfix/t10474

Conversation

@hrhino
Copy link
Contributor

@hrhino hrhino commented Aug 19, 2017

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.

I tried, but I can't find how we can get to cantAdapt with disabled implicits other than by the tricks shown in the test case; running the partests with an assertion that at cantAdapt we either have ImplicitsEnabled or are in QUALmode never trips. The main un-setters of ImplicitsEnabled are typedSingletonTypeTree and typedPattern, which are the two at issue in this PR.

I'm not completely sure about the use of UnstableTreeError as the error; originally I had a CannotSelectFromMethodError but abandoned it in light of:

scala> object Foo { def Bar(i: Int) = i }
defined object Foo

scala> (??? : Any) match { case Foo.Bar.Baz => }
java.lang.NullPointerException
  ... elided ...

scala> (??? : Any) match { case Foo.Bar => }
<console>:13: error: stable identifier required, but Foo.Bar found.
       (??? : Any) match { case Foo.Bar => }
                                    ^

scala> import Foo._
import Foo._

scala> (??? : Any) match { case Bar => }
<console>:16: error: stable identifier required, but Foo.Bar found.
       (??? : Any) match { case Bar => }
                                ^

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.

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.
@scala-jenkins scala-jenkins added this to the 2.12.4 milestone Aug 19, 2017
@adriaanm adriaanm self-requested a review August 24, 2017 22:20
@@ -894,7 +894,10 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper

def cantAdapt =
if (context.implicitsEnabled) MissingArgsForMethodTpeError(tree, meth)
Copy link
Contributor

@adriaanm adriaanm Sep 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Contributor

@adriaanm adriaanm Sep 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        def cantAdapt =
          if (mode.inQualMode) UnstableTreeError(tree)
          else MissingArgsForMethodTpeError(tree, meth)

seems like the way to go EDIT: not

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor

@adriaanm adriaanm Sep 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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."

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

@hrhino hrhino Sep 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(_) => }
                                    ^

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice exploration :-) Agreed on stability -- not really the best thing to complain about here. Oh scalac.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For (??? : Any) match { case Foo => }, the fix could also be turning Foo into a val.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job, I wanted to find some time to fix this issue. Currently, def F(k:Int)=k;val F.K = () throws a NullPointerException.

@adriaanm
Copy link
Contributor

Let's give ourselves some more time to refine this.

@adriaanm adriaanm modified the milestones: 2.12.4, 2.12.5 Sep 27, 2017
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.
@hrhino
Copy link
Contributor Author

hrhino commented Oct 12, 2017

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.

@hrhino
Copy link
Contributor Author

hrhino commented Dec 22, 2017

I think I want to re-think this. I'll re-open with the re-thought. Sorry for putting this off.

@hrhino hrhino closed this Dec 22, 2017
@SethTisue SethTisue removed this from the 2.12.5 milestone Mar 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants