Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions compiler/src/dotty/tools/backend/ScalaPrimitives.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import dotty.tools.dotc.report
import dotty.tools.dotc.util.ReadOnlyMap
import dotty.tools.dotc.ast.Trees.Select
import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.core.Phases

import scala.annotation.threadUnsafe
import scala.annotation.constructorOnly

/** Scala primitive operations are represented as methods in `Any` and
* `AnyVal` subclasses. Here we demultiplex them by providing a mapping
Expand All @@ -30,10 +31,10 @@ import scala.annotation.threadUnsafe
*
* Inspired from the `scalac` compiler.
*/
class ScalaPrimitives(ictx: Context) {
class ScalaPrimitives(using @constructorOnly initCtx: Context) {
import dotty.tools.backend.ScalaPrimitivesOps.*

@threadUnsafe private lazy val primitives: ReadOnlyMap[Symbol, Int] = init
private val primitives: ReadOnlyMap[Symbol, Int] = init

/** Return the code for the given symbol. */
def getPrimitive(sym: Symbol): Int = {
Expand All @@ -49,8 +50,7 @@ class ScalaPrimitives(ictx: Context) {
* @param tpe The type of the receiver object. It is used only for array
* operations
*/
def getPrimitive(app: Apply, tpe: Type): Int = {
given Context = ictx
def getPrimitive(app: Apply, tpe: Type)(using Context): Int = {
val fun = app.fun.symbol
val code = app.fun match {
case Select(_, nme.primitive.arrayLength) =>
Expand Down Expand Up @@ -117,14 +117,13 @@ class ScalaPrimitives(ictx: Context) {
}

/** Initialize the primitive map */
private def init: ReadOnlyMap[Symbol, Int] = {
private def init(using Context): ReadOnlyMap[Symbol, Int] = atPhase(Phases.flattenPhase) {
Copy link
Copy Markdown
Contributor Author

@SolalPirelli SolalPirelli May 28, 2026

Choose a reason for hiding this comment

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

Note the necessary time travel. I think what used to happen is that since primitives was lazy, it would happen to be initialized at the "right" time for the context to be in the right phase.


given Context = ictx
val primitives = MutableSymbolMap[Int](512)

/** Add a primitive operation to the map */
def addPrimitive(s: Symbol, code: Int): Unit = {
assert(!primitives.contains(s), "Duplicate primitive " + s)
assert(!primitives.contains(s), s"Duplicate primitive for code $code: $s")
primitives(s) = code
}

Expand Down Expand Up @@ -397,10 +396,9 @@ class ScalaPrimitives(ictx: Context) {
def isPrimitive(sym: Symbol): Boolean =
primitives.contains(sym)

def isPrimitive(fun: Tree): Boolean =
given Context = ictx
def isPrimitive(fun: Tree)(using Context): Boolean =
primitives.contains(fun.symbol)
|| (fun.symbol == NoSymbol // the only trees that do not have a symbol assigned are array.{update,select,length,clone}}
|| (fun.symbol == NoSymbol // the only trees that do not have a symbol assigned are array.{update,select,length,clone}
&& {
fun match
case Select(_, nme.clone_) => false // but array.clone is NOT a primitive op.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1321,7 +1321,7 @@ trait BCodeBodyBuilder(val primitives: ScalaPrimitives) extends BCodeSkelBuilder
}

/* Is the given symbol a primitive operation? */
def isPrimitive(fun: Tree): Boolean = {
def isPrimitive(fun: Tree)(using Context): Boolean = {
primitives.isPrimitive(fun)
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/backend/jvm/GenBCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class GenBCode extends Phase { self =>
private var _primitives: ScalaPrimitives | Null = null
def primitives(using Context): ScalaPrimitives = {
if _primitives eq null then
_primitives = ScalaPrimitives(ctx)
_primitives = ScalaPrimitives()
_primitives.nn
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class JSCodeGen()(using genCtx: Context) {

val sjsPlatform = dotty.tools.dotc.config.SJSPlatform.sjsPlatform
val jsdefn = JSDefinitions.jsdefn
private val primitives = new JSPrimitives(genCtx)
private val primitives = new JSPrimitives()(using genCtx)

val positionConversions = new JSPositions()(using genCtx)
import positionConversions.*
Expand Down
15 changes: 8 additions & 7 deletions compiler/src/dotty/tools/backend/sjs/JSPrimitives.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import Contexts.*
import Symbols.*
import Decorators.em
import dotty.tools.backend.ScalaPrimitives

import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.report
import dotty.tools.dotc.util.ReadOnlyMap

import scala.annotation.constructorOnly

object JSPrimitives {

inline val FirstJSPrimitiveCode = 300
Expand Down Expand Up @@ -68,22 +69,22 @@ object JSPrimitives {

}

class JSPrimitives(ictx: Context) extends ScalaPrimitives(ictx) {
class JSPrimitives()(using @constructorOnly initCtx: Context) extends ScalaPrimitives {
import JSPrimitives.*

private lazy val jsPrimitives: ReadOnlyMap[Symbol, Int] = initJSPrimitives(using ictx)
private val jsPrimitives: ReadOnlyMap[Symbol, Int] = initJSPrimitives

override def getPrimitive(sym: Symbol): Int =
jsPrimitives.getOrElse(sym, super.getPrimitive(sym))

override def getPrimitive(app: Apply, tpe: Type): Int =
jsPrimitives.getOrElse(app.fun.symbol(using ictx), super.getPrimitive(app, tpe))
override def getPrimitive(app: Apply, tpe: Type)(using Context): Int =
jsPrimitives.getOrElse(app.fun.symbol, super.getPrimitive(app, tpe))

override def isPrimitive(sym: Symbol): Boolean =
jsPrimitives.contains(sym) || super.isPrimitive(sym)

override def isPrimitive(fun: Tree): Boolean =
jsPrimitives.contains(fun.symbol(using ictx)) || super.isPrimitive(fun)
override def isPrimitive(fun: Tree)(using Context): Boolean =
jsPrimitives.contains(fun.symbol) || super.isPrimitive(fun)

/** Initialize the primitive map */
private def initJSPrimitives(using Context): ReadOnlyMap[Symbol, Int] = {
Expand Down
Loading