Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@987Nabil
Copy link
Contributor

@987Nabil 987Nabil commented Feb 16, 2025

fixes #9597
/claim #9597

Copy link
Collaborator

@hearnadam hearnadam left a comment

Choose a reason for hiding this comment

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

I think this is possible without a macro, but I couldn't get the Scope & case to work:

import zio.Scope
import zio.ZEnvironment

abstract class Foo


sealed abstract class HasNoScope[R]
object HasNoScope extends HighPriorityImplicits {
  override protected val instance = new HasNoScope[Any] {}

  def f[R: HasNoScope] = ???


  f[Any] // compiles - correct
  f[Foo & Any] // compiles - correct
  //f[Scope] // doesn't compile - correct
  f[Foo & Scope] // compiles - incorrect

sealed abstract class LowPriorityImplicits {
  protected val instance: HasNoScope[Any]

  @annotation.implicitAmbiguous("Cannot provde that ZEnvironment[${R}] does not contain zio.Scope")
  implicit def supScope1[R >: Scope]: HasNoScope[R] =
    instance.asInstanceOf[HasNoScope[R]]
  implicit def supScope2[R >: Scope]: HasNoScope[R] =
    instance.asInstanceOf[HasNoScope[R]]
}

sealed abstract class MediumPriorityImplicits extends LowPriorityImplicits {
  implicit def hasNoScope[R]: HasNoScope[R] =
    instance.asInstanceOf[HasNoScope[R]]
}
abstract class HighPriorityImplicits extends MediumPriorityImplicits {
  implicit val hasNoScopeAny: HasNoScope[Any] = instance
}

s"Can not prove that $rName does not contain a zio.Scope. Please add a context bound ${rName}: HasNoScope."
)
} else {
'{ HasNoScope.noScope.asInstanceOf[HasNoScope[R]] }
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this can cause some cyclic issues in edge cases. My suggestion would be to use protected def instance and the object can override with a val

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't get the override val part


import scala.quoted._

private[zio] trait HasNoScopeCompanionVersionSpecific {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
private[zio] trait HasNoScopeCompanionVersionSpecific {
private[zio] abstract class HasNoScopeCompanionVersionSpecific {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why?

Copy link
Collaborator

Choose a reason for hiding this comment

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

traits are compiled to interfaces which can introduce interface dispatch. In this case, it's probably equivalent. However, it's a good precedence to set going forward. It's not binary compat to go from class -> trait, but it's easy to go the other way (if necessary).

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

private[zio] trait HasNoScopeCompanionVersionSpecific {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
private[zio] trait HasNoScopeCompanionVersionSpecific {
private[zio] abstract class HasNoScopeCompanionVersionSpecific {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why?

sealed trait HasNoScope[R]

object HasNoScope extends HasNoScopeCompanionVersionSpecific {
val noScope: HasNoScope[Any] = new HasNoScope[Any] {}
Copy link
Collaborator

Choose a reason for hiding this comment

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

private/protected

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am quite sure, that won't work. Because the access will be inlined and the place of inlining won't have the correct access rights

Copy link
Contributor

@kyri-petrou kyri-petrou Feb 18, 2025

Choose a reason for hiding this comment

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

If you make it package-private it'll work. package-private methods don't exist on the JVM so they're compiled to public methods.

PS: It will also work if you make it private; but in that case the Scala compiler will create a public proxy method which we probably want to avoid

@987Nabil
Copy link
Contributor Author

I think this is possible without a macro, but I couldn't get the Scope & case to work:

import zio.Scope
import zio.ZEnvironment

abstract class Foo


sealed abstract class HasNoScope[R]
object HasNoScope extends HighPriorityImplicits {
  override protected val instance = new HasNoScope[Any] {}

  def f[R: HasNoScope] = ???


  f[Any] // compiles - correct
  f[Foo & Any] // compiles - correct
  //f[Scope] // doesn't compile - correct
  f[Foo & Scope] // compiles - incorrect

sealed abstract class LowPriorityImplicits {
  protected val instance: HasNoScope[Any]

  @annotation.implicitAmbiguous("Cannot provde that ZEnvironment[${R}] does not contain zio.Scope")
  implicit def supScope1[R >: Scope]: HasNoScope[R] =
    instance.asInstanceOf[HasNoScope[R]]
  implicit def supScope2[R >: Scope]: HasNoScope[R] =
    instance.asInstanceOf[HasNoScope[R]]
}

sealed abstract class MediumPriorityImplicits extends LowPriorityImplicits {
  implicit def hasNoScope[R]: HasNoScope[R] =
    instance.asInstanceOf[HasNoScope[R]]
}
abstract class HighPriorityImplicits extends MediumPriorityImplicits {
  implicit val hasNoScopeAny: HasNoScope[Any] = instance
}

The macro works and is on the simple side of macros. I think it is okay.
Also I think you still miss one point. That is a generic method calling the use side of the :HasNoScope context bound.
This is in the test, but not here. The macro forces you to propagate an implicit HasNoScope[R] from a place where R is a non generic type.

@@ -0,0 +1,56 @@
package zio.managed
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like this is the wrong place with this suite. Any reason it's not under the core-tests folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just took a look where the other tests that use macros where. I can move it

Copy link
Contributor

@kyri-petrou kyri-petrou Feb 18, 2025

Choose a reason for hiding this comment

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

Yes let's please move it. I'm not even sure why the other macro tests are under there but we should probably move those too (not in this PR ofc)

Copy link
Collaborator

@hearnadam hearnadam left a comment

Choose a reason for hiding this comment

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

Code LGTM, as discussed over Discord I think Scala 3's typecheck should be a transparent inline to correctly show the macro errors. That would allow unification of suites.

sealed trait HasNoScope[R]

object HasNoScope extends HasNoScopeCompanionVersionSpecific {
override val instance: HasNoScope[Any] = new HasNoScope[Any] {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we change this to package-private?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This will be called by the inlined code of the macro. If it is not public, the compiler will fail

Copy link
Contributor

Choose a reason for hiding this comment

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

package-private methods / vals are public when compiled. Have you tried changing it to package-private and it didn't work?

@kyri-petrou kyri-petrou merged commit f080050 into zio:series/2.x Feb 19, 2025
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create type class HasNoScope[R] for checking that R does not contain Scope

5 participants