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

SI-10097 Error if no non-implicit case class param #5585

Merged
merged 4 commits into from
Feb 8, 2017

Conversation

som-snytt
Copy link
Contributor

@som-snytt som-snytt commented Dec 7, 2016

Case class must have a non-implicit param list.

Error early, error often.

JIRA: https://issues.scala-lang.org/browse/SI-10097

@scala-jenkins scala-jenkins added this to the 2.12.2 milestone Dec 7, 2016
@som-snytt
Copy link
Contributor Author

Destructuring in reflection also makes assumption about empty first param list that must be tweaked for case class. UnMkTemplate must be case-class aware. That changes tree printing.

@szeiger
Copy link
Contributor

szeiger commented Dec 8, 2016

The spec doesn't mention any restrictions on case class parameter lists. I think they should be added. (This also goes for the existing restriction on requiring a parameter list at all)

@som-snytt som-snytt force-pushed the issue/10097 branch 3 times, most recently from 9b2492b to 8549945 Compare December 8, 2016 19:48
syntaxError(warnAt, "multiple implicit parameter sections are not allowed")
else {
// guard against anomalous class C(private implicit val x: Int)(implicit s: String)
val ttl = vds.count(_ match { case ValDef(mods, _, _, _) :: _ => mods.isImplicit ; case _ => false })
Copy link
Contributor Author

Choose a reason for hiding this comment

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

git push to clean this syntax is hanging for some reason. Try again later.

@som-snytt som-snytt force-pushed the issue/10097 branch 2 times, most recently from 21d8b40 to 1656251 Compare December 9, 2016 09:58
@lrytz
Copy link
Member

lrytz commented Dec 9, 2016

Looks very good. I think the last commit should be an error, and not only a warning under a flag. Can we change the parser to make a parameter list implicit also if the implicit modifier is not the first? The following should be an error IMO (also the test case you added):

scala> class Ann extends annotation.Annotation
defined class Ann

scala> class D(@Ann implicit val i: Int)(implicit s: String)
defined class D

Finally, I think we should be careful to introduce this (require () for case classes) into a minor release, it should probably target 2.13.x

@som-snytt
Copy link
Contributor Author

som-snytt commented Dec 9, 2016

I think the bug is typer taking a param section as implicit if the first param is implicit.

That's why (as an afterthought) I put the noise about "effectively multiple" under a flag; I also considered -Xlint. This is really an implementation restriction and not in the spec.

paulp said on gitter that implicit mod on a param is a non-feature. But people like { implicit x => } syntax. It occurs to me that self-type syntax could work: class C(c: Int) { self(implicit c) => } with the goal of avoiding turning the param into a field such as: class C(c0: Int) { private implicit val c = c0; }. For a function param, an extra local is more benign. def f(c0: Int) { implicit val c = c0; ... }.

But maybe that is syntax in the service of a non-feature.

For 2.12 migration of missing non-implicit case class params, it can silently add the empty param section, but error under -Xfuture. But it should probably warn with deprecation.

One of the tests used a space to indicate a missing param list. Since the syntax showed up in a couple of tests, probably it exists in the wild (and not just in Bippy).

@lrytz
Copy link
Member

lrytz commented Dec 12, 2016

OK, I should have checked the spec earlier. I didn't realize that implicit is a valid modifier for class parameters, and if it doesn't appear at the beginning of the parameter list, it just applies to one specific parameter.

You say in one of the commit messages:

Current semantics are that leading implicit param turns the parameter section into an implicit section (though without making other params implicitly implicit).

That sounds like a bug, shouldn't we fix this instead?

Instead of -Xfuture, should we use -Xsource:2.13 so that things will just work when the future arrives?

In the REPL i get two warnings:

scala> case class C(implicit i: Int)
<console>:1: warning: case classes should have a non-implicit parameter list; adapting to 'case class C()(...)'
case class C(implicit i: Int)
            ^
<console>:11: warning: case classes should have a non-implicit parameter list; adapting to 'case class C()(...)'
       case class C(implicit i: Int)
                   ^

Also the warning text adapting to 'case class C()(...)' is not entirely accurate:

scala> C()(1).productIterator.toList
res0: List[Any] = List(1)

scala> case class C()(implicit i: Int)
defined class C

scala> C()(1).productIterator.toList
res1: List[Any] = List()

Case class must have a non-implicit param list.

Error early, error often.

Also update spec to say that class implicitly gets
a non-implicit parameter section if it doesn't
have one, and that a case class must have one.
Instead of aborting when a class def has extra
parameter section, take all parameter sections
and sanity check the use of leading implicit
to indicate an implicit parameter section.
Current semantics are that leading implicit param
turns the parameter section into an implicit section
(though without making other params implicitly
implicit).

Warn if more than one head of a param section is
implicit, since that results in multiple implicit
param sections.
@som-snytt
Copy link
Contributor Author

The perils of relying on slave labor:

Slave went offline during the build
ERROR: Connection was broken: java.io.IOException: Unexpected termination of the channel
	at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:50)
Caused by: java.io.EOFException
	at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2353)
	at java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2822)
	at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:804)
	at java.io.ObjectInputStream.<init>(ObjectInputStream.java:301)
	at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:48)
	at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
	at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)

@som-snytt
Copy link
Contributor Author

/rebuild

For 2.12 migration, insert missing case class param section,
strip caseaccessor from implicit paramsection,
and deprecate the adaptation.
@retronym
Copy link
Member

IntelliJ seems to implement automatic insertion of the empty param lists (consistently with non-case classes), and gets the copy method and absence of accessor method correct.

image

image

If we go ahead with this change, /cc @Alefas to add the deprecation to IntelliJ 2.12 and the error to IntelliJ 2.13.

@retronym
Copy link
Member

However, I'm not sure that we should treat case and non-case classes differently like this. We could just insert the empty parameter list, like we do for regular classes, and make sure that the other case class machinery is aware of this.

Maybe we can argue in favor of the the inconsistency because of fact that we're just fixing an existing restriction:

scala> case class Foo
<console>:1: error: case classes without a parameter list are not allowed;
use either case objects or case classes with an explicit `()' as a parameter list.
case class Foo
              ^

scala> case class Foo()
defined class Foo

Rather than adding a new one.

With that in mind:

LGTM

@retronym retronym self-requested a review December 20, 2016 01:49
@som-snytt
Copy link
Contributor Author

IIUC, you mean the transitional behavior of assuming empty parens for case class K(implicit k: K) should be the default, without warning. I think the combination of Lukas's suggestion to push the error to 2.13 plus the intuition that K is probably an error (either oversight or misunderstanding) led to this proposal.

If K looks natural, then I agree that just supplying parens is sufficient. But then shouldn't case class C also supply parens? In 2017, parameterless is no more naive than non-implicitless. It used to look naive because the community was still coming to terms with what a case class can do. (Such as inheritance.)

I'll push whichever warning the Scala team prefers. I guess that's why the Scala team always has an odd number of members, so Martin needn't break any ties. (Including personal ties.)

@retronym
Copy link
Member

In the end I'm happy to impose the new restriction, I was thinking out loud.

@Alefas
Copy link

Alefas commented Dec 21, 2016

@retronym I created new ticket for us: https://youtrack.jetbrains.com/issue/SCL-11169
Big thanks for the notice.

@som-snytt
Copy link
Contributor Author

There's an issue for "where implicit appears in front of first class param" (which lrytz suggests fixing above):

https://issues.scala-lang.org/browse/SI-9494

There's also SIP for multi-implicits:

scala/docs.scala-lang#520

@adriaanm adriaanm merged commit 40d01f3 into scala:2.12.x Feb 8, 2017
@som-snytt som-snytt deleted the issue/10097 branch February 8, 2017 03:17
@milessabin
Copy link
Contributor

I agree that SI-10097 needs fixing, but I'm unclear why this is the correct fix. Why isn't an implicit parameter list considered to be the parameter list required for a case class?

@SethTisue
Copy link
Member

Why isn't an implicit parameter list considered to be the parameter list required for a case class?

you really want to do that? (or think someone else would?)

@som-snytt
Copy link
Contributor Author

One justification is so that a "constructor pattern" looks like a constructor invocation.

The abstract case class trick lets you control apply but not unapply. Maybe an ergonomic solution to that would satisfy the implicit case elements use case.

A second justification might be that capturing implicits as vals is unexpected?

@som-snytt
Copy link
Contributor Author

@lrytz The extra warnings were from parsing, SI-10130 #5647

@som-snytt
Copy link
Contributor Author

Another case class anomaly is case class C(private[this] val c: Int). That's SI-1422. It was closed for pragmatic reasons. Since accessors are synthesized for private members, for purposes of pattern matching, it should also be OK for object private. So case classes deviate from ordinary classes in ways that may or may not be justified.

@milessabin
Copy link
Contributor

On reflection, an implicit first (and only) parameter list for a case class isn't particularly useful because the bare name will always be interpreted as a reference to the companion object.

So, fine by me.

@SethTisue
Copy link
Member

the Scala team always has an odd number of members

we also have a number of odd members.

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.

9 participants