Skip to content

Commit

Permalink
Fix issue with static this references erroring in quoted code (#22618)
Browse files Browse the repository at this point in the history
Previously, inherited methods, even if accessed via static objects, were
not able to be used in quotations, unless explicitly pointed to with a
non-`this` prefix. This was due to the fact that during the cross-stage
safety check, first the method itself was checked for if it was static
(which for the inherited method, it was not), and if not, the prefix was
checked further, erroring on any `this` tree found along the way.

This was fixed by allowing `this` trees if they point to static objects.
This way not only is the initial issue fixed, but also we are able to
freely reference static methods with `this`, like `'{this.objectMethod}`
(whereas previously, only `'{Object.objectMethod}` or `'{objectMethod}`
were allowed, despite them all pointing to the same static method).

Fixes #22592
  • Loading branch information
hamzaremmal authored Feb 20, 2025
2 parents 345b2da + 71ddfb5 commit 3ae11ab
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 1 deletion.
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala
Original file line number Diff line number Diff line change
Expand Up @@ -203,19 +203,27 @@ class CrossStageSafety extends TreeMapWithStages {

/** Check level consistency of terms references */
private def checkLevelConsistency(tree: Ident | This)(using Context): Unit =
def isStatic(pre: Type)(using Context): Boolean = pre match
case pre: NamedType =>
val sym = pre.currentSymbol
sym.is(Package) || sym.isStaticOwner && isStatic(pre.prefix)
case pre: ThisType => isStatic(pre.tref)
case _ => true
new TypeTraverser {
def traverse(tp: Type): Unit =
tp match
case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level != levelOf(tp.symbol) =>
levelError(tp.symbol, tp, tree.srcPos)
case tp: ThisType if isStatic(tp) =>
// static object (OK)
case tp: ThisType if level != -1 && level != levelOf(tp.cls) =>
levelError(tp.cls, tp, tree.srcPos)
case tp: AnnotatedType =>
traverse(tp.parent)
case _ if tp.typeSymbol.is(Package) =>
// OK
case _ =>
traverseChildren(tp)
traverseChildren(tp)
}.traverse(tree.tpe)

private def levelError(sym: Symbol, tp: Type, pos: SrcPos)(using Context): tp.type = {
Expand Down
14 changes: 14 additions & 0 deletions tests/neg/i22592.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import scala.quoted.*

trait Foo:
def inherited = ()

class Bar extends Foo:
def local = ()
def localArg(arg: Any) = ()

def macro1(using Quotes): Expr[Unit] = '{ local } // error
def macro3(using Quotes): Expr[Unit] = '{ inherited } // error
def macro4(using Quotes): Expr[Unit] = '{ this.local } // error
def macro5(using Quotes): Expr[Unit] = '{ this.inherited } // error
def macro6(using Quotes): Expr[Unit] = '{ localArg(this) } // error // error
15 changes: 15 additions & 0 deletions tests/pos/i22592.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.quoted.*

trait Foo:
def inherited = ()

object Bar extends Foo:
def local = ()
def localArg(arg: Any) = ()

def macro1(using Quotes): Expr[Unit] = '{ local }
def macro2(using Quotes): Expr[Unit] = '{ Bar.inherited }
def macro3(using Quotes): Expr[Unit] = '{ inherited }
def macro4(using Quotes): Expr[Unit] = '{ this.local }
def macro5(using Quotes): Expr[Unit] = '{ this.inherited }
def macro6(using Quotes): Expr[Unit] = '{ localArg(this) }

0 comments on commit 3ae11ab

Please sign in to comment.