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

selectDynamic cannot be an extension method #17100

Closed
prolativ opened this issue Mar 14, 2023 · 11 comments · Fixed by #17106
Closed

selectDynamic cannot be an extension method #17100

prolativ opened this issue Mar 14, 2023 · 11 comments · Fixed by #17106
Assignees
Milestone

Comments

@prolativ
Copy link
Contributor

Compiler version

3.3.1-RC1-bin-20230313-f28d708-NIGHTLY and before

Minimized code

trait Sel extends Selectable

extension (s: Sel)
  def selectDynamic(name: String) = ???

val sel = (new Sel {}).asInstanceOf[Sel{ def foo: String }]
val foo = sel.foo

Output

[error] ./Selectable.scala:7:11
[error] value selectDynamic is not a member of Sel{def foo: String}, but could be made available as an extension method.
[error] 
[error] The following import might fix the problem:
[error] 
[error]   import reflect.Selectable.reflectiveSelectable
[error] 
[error] val foo = sel.foo
[error]           ^^^

Expectation

According to our reference documentation:

Implementations of Selectable have to make available one or both of the methods selectDynamic and applyDynamic. The methods could be members of the Selectable implementation or they could be extension methods.

To my understanding this means that the code snippet should compile successfully, by applying the syntax desugarings:

sel.foo

sel.selectDynamic("foo").asInstanceOf[String]

selectDynamic(sel)("foo").asInstanceOf[String]

Similarly it should be possible to provide the implementation of applyDynamic as an extension method.

@prolativ
Copy link
Contributor Author

@odersky did I get the specification right? The only case that works and that I know about in which a structural selection is desugared to selectDynamic even though the receiver does not inherit from scala.Selectable is the case of scala.reflect.Selectable. But this works in the way that a refined type which does NOT extends scala.Selectable is converted (via an implicit conversion) to scala.reflect.Selectable (or more specifically to its subtype scala.reflect.Selectable.DefaultSelectable), which actually does implement selectDynamic as a member method.

@odersky
Copy link
Contributor

odersky commented Mar 15, 2023

Yes, this should work.

odersky added a commit to dotty-staging/dotty that referenced this issue Mar 15, 2023
Allow selectDynamic and applyDynamic to be an extension methods when dispatching structurally.

Fixes scala#17100
@plokhotnyuk
Copy link

Should it work for extension of Dynamic like in this issue?

@odersky
Copy link
Contributor

odersky commented Mar 15, 2023

No

@plokhotnyuk
Copy link

plokhotnyuk commented Mar 15, 2023

Just to be clear.

If I will replace Selectable by Dynamic should it work for the following code too?

trait Sel extends Dynamic

extension (s: Sel)
  def selectDynamic(name: String) = ???

val sel = (new Sel {}).asInstanceOf[Sel{ def foo: String }]
val foo = sel.foo

@prolativ
Copy link
Contributor Author

@plokhotnyuk Actually in case of Dynamic this seems to work (checked with 3.nightly and 3.0.0 so I assume other versions are OK too):

import scala.language.dynamics

trait Sel extends Dynamic

extension (s: Sel)
  def selectDynamic(name: String) = name

val sel = new Sel {}
val foo = sel.foo

The point is that you need to import scala.language.dynamics and you should NOT explicitly add the member declaration in a refinement (because Dynamic is used for members that are not statically guaranteed to exist).

@odersky
Copy link
Contributor

odersky commented Mar 15, 2023

@prolativ Yes, that's the current case. But it seems reasonable to also allow Dynamic even if the qualifier type is structural. I am trying to to this in 2808f3d.

odersky added a commit to dotty-staging/dotty that referenced this issue Mar 15, 2023
Allow selectDynamic and applyDynamic to be an extension methods when dispatching structurally.

Fixes scala#17100
@prolativ
Copy link
Contributor Author

@odersky interestingly this code

import scala.language.dynamics

trait Sel extends Dynamic {
  def selectDynamic(name: String) = name
}

object Test {
  val sel1 = new Sel {}
  val foo1 = sel1.foo
  val sel2 = (new Sel {}).asInstanceOf[Sel{ def foo: String }]
  val foo2 = sel2.foo
}

does compile in 2.13.10 but it raises a warning:

[warn] ./Sel.scala:11:14
[warn] reflective access of structural type member method foo should be enabled
[warn] by making the implicit value scala.language.reflectiveCalls visible.
[warn] ----
[warn] This can be achieved by adding the import clause 'import scala.language.reflectiveCalls'
[warn] or by setting the compiler option -language:reflectiveCalls.
[warn] See the Scaladoc for value scala.language.reflectiveCalls for a discussion
[warn] why the feature should be explicitly enabled.
[warn]   val foo2 = sel2.foo
[warn]              ^^^^^^^^

so there seems to be a slight semantic difference between sel1.foo and sel2.foo in scala 2.
So just to be clear: do we want these two cases to behave exactly in the same way in scala 3 regardless of the difference in scala 2?

@odersky
Copy link
Contributor

odersky commented Mar 15, 2023

No, structural types are one of the areas where Scala 3 is different. Generally, reflectiveCalls is no longer needed in Scala 3.

@odersky
Copy link
Contributor

odersky commented Mar 15, 2023

The tests fail with

Test dotty.tools.coursier.CoursierScalaTests.allTests failed: java.io.IOException: Cannot run program "./cs": error=2, No such file or directory, took 0.12 sec

Can someone explain what this is or how to fix it?

@prolativ
Copy link
Contributor Author

This seems to be some problem with our CI infrastructure, present since yesterday. There's a chance rerunning the jobs might help

odersky added a commit that referenced this issue Mar 17, 2023
Allow selectDynamic and applyDynamic to be extension methods when
dispatching structurally.

Fixes #17100
Dedelweiss pushed a commit to Dedelweiss/dotty that referenced this issue Apr 17, 2023
Allow selectDynamic and applyDynamic to be an extension methods when dispatching structurally.

Fixes scala#17100
@Kordyjan Kordyjan added this to the 3.3.1 milestone Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants