Skip to content

Commit

Permalink
Merge branch 'main' into update-scala-settings
Browse files Browse the repository at this point in the history
  • Loading branch information
rochala authored May 6, 2024
2 parents ad81b6f + 1cdf99f commit ca916f0
Show file tree
Hide file tree
Showing 65 changed files with 1,161 additions and 280 deletions.
84 changes: 55 additions & 29 deletions compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -927,7 +927,7 @@ class JSCodeGen()(using genCtx: Context) {
val className = encodeClassName(classSym)
val body = js.Block(
js.LoadModule(className),
js.SelectStatic(className, fieldIdent)(irTpe))
js.SelectStatic(fieldIdent)(irTpe))
staticGetterDefs += js.MethodDef(
js.MemberFlags.empty.withNamespace(js.MemberNamespace.PublicStatic),
encodeStaticMemberSym(f), originalName, Nil, irTpe,
Expand Down Expand Up @@ -1146,42 +1146,72 @@ class JSCodeGen()(using genCtx: Context) {

private def genPrimaryJSClassCtor(dd: DefDef): PrimaryJSCtor = {
val sym = dd.symbol
val Block(stats, _) = dd.rhs: @unchecked
assert(sym.isPrimaryConstructor, s"called with non-primary ctor: $sym")

var preSuperStats = List.newBuilder[js.Tree]
var jsSuperCall: Option[js.JSSuperConstructorCall] = None
val jsStats = List.newBuilder[js.Tree]
val postSuperStats = List.newBuilder[js.Tree]

/* Move all statements after the super constructor call since JS
* cannot access `this` before the super constructor call.
/* Move param accessor initializers after the super constructor call since
* JS cannot access `this` before the super constructor call.
*
* dotc inserts statements before the super constructor call for param
* accessor initializers (including val's and var's declared in the
* params). We move those after the super constructor call, and are
* therefore executed later than for a Scala class.
* params). Those statements are assignments whose rhs'es are always simple
* Idents (the constructor params).
*
* There can also be local `val`s before the super constructor call for
* default arguments to the super constructor. These must remain before.
*
* Our strategy is therefore to move only the field assignments after the
* super constructor call. They are therefore executed later than for a
* Scala class (as specified for non-native JS classes semantics).
* However, side effects and evaluation order of all the other
* computations remains unchanged.
*/
withPerMethodBodyState(sym) {
stats.foreach {
case tree @ Apply(fun @ Select(Super(This(_), _), _), args)
if fun.symbol.isClassConstructor =>
assert(jsSuperCall.isEmpty, s"Found 2 JS Super calls at ${dd.sourcePos}")
implicit val pos: Position = tree.span
jsSuperCall = Some(js.JSSuperConstructorCall(genActualJSArgs(fun.symbol, args)))
def isThisField(tree: Tree): Boolean = tree match {
case Select(ths: This, _) => ths.symbol == currentClassSym.get
case tree: Ident => desugarIdent(tree).exists(isThisField(_))
case _ => false
}

case stat =>
val jsStat = genStat(stat)
assert(jsSuperCall.isDefined || !jsStat.isInstanceOf[js.VarDef],
"Trying to move a local VarDef after the super constructor call of a non-native JS class at " +
dd.sourcePos)
jsStats += jsStat
def rec(tree: Tree): Unit = {
tree match {
case Block(stats, expr) =>
stats.foreach(rec(_))
rec(expr)

case tree @ Apply(fun @ Select(Super(This(_), _), _), args)
if fun.symbol.isClassConstructor =>
assert(jsSuperCall.isEmpty, s"Found 2 JS Super calls at ${dd.sourcePos}")
implicit val pos: Position = tree.span
jsSuperCall = Some(js.JSSuperConstructorCall(genActualJSArgs(fun.symbol, args)))

case tree if jsSuperCall.isDefined =>
// Once we're past the super constructor call, everything goes after.
postSuperStats += genStat(tree)

case Assign(lhs, Ident(_)) if isThisField(lhs) =>
/* If that shape appears before the jsSuperCall, it is a param
* accessor initializer. We move it.
*/
postSuperStats += genStat(tree)

case stat =>
// Other statements are left before.
preSuperStats += genStat(stat)
}
}

rec(dd.rhs)
}

assert(jsSuperCall.isDefined,
s"Did not find Super call in primary JS construtor at ${dd.sourcePos}")

new PrimaryJSCtor(sym, genParamsAndInfo(sym, dd.paramss),
js.JSConstructorBody(Nil, jsSuperCall.get, jsStats.result())(dd.span))
js.JSConstructorBody(preSuperStats.result(), jsSuperCall.get, postSuperStats.result())(dd.span))
}

private def genSecondaryJSClassCtor(dd: DefDef): SplitSecondaryJSCtor = {
Expand Down Expand Up @@ -2213,10 +2243,7 @@ class JSCodeGen()(using genCtx: Context) {
if (isStaticModule(currentClassSym) && !isModuleInitialized.get.value &&
currentMethodSym.get.isClassConstructor) {
isModuleInitialized.get.value = true
val className = encodeClassName(currentClassSym)
val thisType = jstpe.ClassType(className)
val initModule = js.StoreModule(className, js.This()(thisType))
js.Block(superCall, initModule)
js.Block(superCall, js.StoreModule())
} else {
superCall
}
Expand Down Expand Up @@ -4433,13 +4460,12 @@ class JSCodeGen()(using genCtx: Context) {
js.JSSelect(qual, genPrivateFieldsSymbol()),
encodeFieldSymAsStringLiteral(sym))
} else {
js.JSPrivateSelect(qual, encodeClassName(sym.owner),
encodeFieldSym(sym))
js.JSPrivateSelect(qual, encodeFieldSym(sym))
}

(f, true)
} else if (sym.hasAnnotation(jsdefn.JSExportTopLevelAnnot)) {
val f = js.SelectStatic(encodeClassName(sym.owner), encodeFieldSym(sym))(jstpe.AnyType)
val f = js.SelectStatic(encodeFieldSym(sym))(jstpe.AnyType)
(f, true)
} else if (sym.hasAnnotation(jsdefn.JSExportStaticAnnot)) {
val jsName = sym.getAnnotation(jsdefn.JSExportStaticAnnot).get.argumentConstantString(0).getOrElse {
Expand All @@ -4465,9 +4491,9 @@ class JSCodeGen()(using genCtx: Context) {

val f =
if sym.is(JavaStatic) then
js.SelectStatic(className, fieldIdent)(irType)
js.SelectStatic(fieldIdent)(irType)
else
js.Select(qual, className, fieldIdent)(irType)
js.Select(qual, fieldIdent)(irType)

(f, boxed)
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/backend/sjs/JSEncoding.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import dotty.tools.dotc.transform.sjs.JSSymUtils.*

import org.scalajs.ir
import org.scalajs.ir.{Trees => js, Types => jstpe}
import org.scalajs.ir.Names.{LocalName, LabelName, FieldName, SimpleMethodName, MethodName, ClassName}
import org.scalajs.ir.Names.{LocalName, LabelName, SimpleFieldName, FieldName, SimpleMethodName, MethodName, ClassName}
import org.scalajs.ir.OriginalName
import org.scalajs.ir.OriginalName.NoOriginalName
import org.scalajs.ir.UTF8String
Expand Down Expand Up @@ -173,7 +173,7 @@ object JSEncoding {
}

def encodeFieldSym(sym: Symbol)(implicit ctx: Context, pos: ir.Position): js.FieldIdent =
js.FieldIdent(FieldName(encodeFieldSymAsString(sym)))
js.FieldIdent(FieldName(encodeClassName(sym.owner), SimpleFieldName(encodeFieldSymAsString(sym))))

def encodeFieldSymAsStringLiteral(sym: Symbol)(implicit ctx: Context, pos: ir.Position): js.StringLiteral =
js.StringLiteral(encodeFieldSymAsString(sym))
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/SourceVersion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum SourceVersion:
case `3.3-migration`, `3.3`
case `3.4-migration`, `3.4`
case `3.5-migration`, `3.5`
case `3.6-migration`, `3.6`
// !!! Keep in sync with scala.runtime.stdlibPatches.language !!!
case `future-migration`, `future`

Expand Down
19 changes: 12 additions & 7 deletions compiler/src/dotty/tools/dotc/core/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ object Mode {
val Pattern: Mode = newMode(0, "Pattern")
val Type: Mode = newMode(1, "Type")

val PatternOrTypeBits: Mode = Pattern | Type

val ImplicitsEnabled: Mode = newMode(2, "ImplicitsEnabled")
val InferringReturnType: Mode = newMode(3, "InferringReturnType")

Expand Down Expand Up @@ -101,16 +103,19 @@ object Mode {
*/
val CheckBoundsOrSelfType: Mode = newMode(14, "CheckBoundsOrSelfType")

/** Use Scala2 scheme for overloading and implicit resolution */
val OldOverloadingResolution: Mode = newMode(15, "OldOverloadingResolution")
/** Use previous Scheme for implicit resolution. Currently significant
* in 3.0-migration where we use Scala-2's scheme instead and in 3.5-migration
* where we use the previous scheme up to 3.4 instead.
*/
val OldImplicitResolution: Mode = newMode(15, "OldImplicitResolution")

/** Treat CapturingTypes as plain AnnotatedTypes even in phase CheckCaptures.
* Reuses the value of OldOverloadingResolution to save Mode bits.
* This is OK since OldOverloadingResolution only affects implicit search, which
* Reuses the value of OldImplicitResolution to save Mode bits.
* This is OK since OldImplicitResolution only affects implicit search, which
* is done during phases Typer and Inlinig, and IgnoreCaptures only has an
* effect during phase CheckCaptures.
*/
val IgnoreCaptures = OldOverloadingResolution
val IgnoreCaptures = OldImplicitResolution

/** Allow hk applications of type lambdas to wildcard arguments;
* used for checking that such applications do not normally arise
Expand All @@ -120,8 +125,6 @@ object Mode {
/** Read original positions when unpickling from TASTY */
val ReadPositions: Mode = newMode(17, "ReadPositions")

val PatternOrTypeBits: Mode = Pattern | Type

/** We are elaborating the fully qualified name of a package clause.
* In this case, identifiers should never be imported.
*/
Expand All @@ -133,6 +136,8 @@ object Mode {
/** We are typing the body of an inline method */
val InlineableBody: Mode = newMode(21, "InlineableBody")

val NewGivenRules: Mode = newMode(22, "NewGivenRules")

/** We are synthesizing the receiver of an extension method */
val SynthesizeExtMethodReceiver: Mode = newMode(23, "SynthesizeExtMethodReceiver")

Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ object Types extends TypeUtils {
* | +- TypeVar
* | +- HKTypeLambda
* | +- MatchType
* | +- FlexibleType
* |
* +- GroundType -+- AndType
* +- OrType
Expand Down Expand Up @@ -3468,7 +3469,7 @@ object Types extends TypeUtils {
* `T | Null .. T`, so that `T | Null <: FlexibleType(T) <: T`.
* A flexible type will be erased to its original type `T`.
*/
case class FlexibleType(lo: Type, hi: Type) extends CachedProxyType with ValueType {
case class FlexibleType protected(lo: Type, hi: Type) extends CachedProxyType with ValueType {

override def underlying(using Context): Type = hi

Expand All @@ -3481,7 +3482,7 @@ object Types extends TypeUtils {
}

object FlexibleType {
def apply(tp: Type)(using Context): Type = tp match {
def apply(tp: Type)(using Context): FlexibleType = tp match {
case ft: FlexibleType => ft
case _ =>
// val tp1 = tp.stripNull()
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/inlines/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ class Inliner(val call: tpd.Tree)(using Context):
case Super(qual, _) => qual
case pre => pre
val preLevel = classNestingLevel(inlinedMethod.owner)
if preLevel > level then outerSelect(pre, inlinedMethod.owner, preLevel - level, selfSym.info)
if preLevel > level then outerSelect(pre, inlinedMethod.owner.enclosingClass, preLevel - level, selfSym.info)
else pre

val binding = accountForOpaques(
Expand Down
31 changes: 24 additions & 7 deletions compiler/src/dotty/tools/dotc/interactive/Completion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ object Completion:
customMatcher: Option[Name => Boolean] = None
)(using Context): CompletionMap =
val adjustedPath = typeCheckExtensionConstructPath(untpdPath, tpdPath, pos)
computeCompletions(pos, mode, rawPrefix, adjustedPath, customMatcher)
computeCompletions(pos, mode, rawPrefix, adjustedPath, untpdPath, customMatcher)

/**
* Inspect `path` to determine what kinds of symbols should be considered.
Expand Down Expand Up @@ -199,12 +199,16 @@ object Completion:
.flatten.getOrElse(tpdPath)

private def computeCompletions(
pos: SourcePosition, mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree], matches: Option[Name => Boolean]
pos: SourcePosition,
mode: Mode, rawPrefix: String,
adjustedPath: List[tpd.Tree],
untpdPath: List[untpd.Tree],
matches: Option[Name => Boolean]
)(using Context): CompletionMap =
val hasBackTick = rawPrefix.headOption.contains('`')
val prefix = if hasBackTick then rawPrefix.drop(1) else rawPrefix
val matches0 = matches.getOrElse(_.startsWith(prefix))
val completer = new Completer(mode, pos, matches0)
val completer = new Completer(mode, pos, untpdPath, matches0)

val result = adjustedPath match
// Ignore synthetic select from `This` because in code it was `Ident`
Expand Down Expand Up @@ -279,6 +283,12 @@ object Completion:
if denot.isType then denot.symbol.showFullName
else denot.info.widenTermRefExpr.show


def isInNewContext(untpdPath: List[untpd.Tree]): Boolean =
untpdPath match
case _ :: untpd.New(selectOrIdent: (untpd.Select | untpd.Ident)) :: _ => true
case _ => false

/** Include in completion sets only symbols that
* 1. is not absent (info is not NoType)
* 2. are not a primary constructor,
Expand All @@ -290,7 +300,11 @@ object Completion:
* 8. symbol is not a constructor proxy module when in type completion mode
* 9. have same term/type kind as name prefix given so far
*/
def isValidCompletionSymbol(sym: Symbol, completionMode: Mode)(using Context): Boolean =
def isValidCompletionSymbol(sym: Symbol, completionMode: Mode, isNew: Boolean)(using Context): Boolean =

lazy val isEnum = sym.is(Enum) ||
(sym.companionClass.exists && sym.companionClass.is(Enum))

sym.exists &&
!sym.isAbsent() &&
!sym.isPrimaryConstructor &&
Expand All @@ -300,6 +314,7 @@ object Completion:
!sym.isPackageObject &&
!sym.is(Artifact) &&
!(completionMode.is(Mode.Type) && sym.isAllOf(ConstructorProxyModule)) &&
!(isNew && isEnum) &&
(
(completionMode.is(Mode.Term) && (sym.isTerm || sym.is(ModuleClass))
|| (completionMode.is(Mode.Type) && (sym.isType || sym.isStableMember)))
Expand All @@ -323,7 +338,7 @@ object Completion:
* For the results of all `xyzCompletions` methods term names and type names are always treated as different keys in the same map
* and they never conflict with each other.
*/
class Completer(val mode: Mode, pos: SourcePosition, matches: Name => Boolean):
class Completer(val mode: Mode, pos: SourcePosition, untpdPath: List[untpd.Tree], matches: Name => Boolean):
/** Completions for terms and types that are currently in scope:
* the members of the current class, local definitions and the symbols that have been imported,
* recursively adding completions from outer scopes.
Expand Down Expand Up @@ -530,7 +545,7 @@ object Completion:
// There are four possible ways for an extension method to be applicable

// 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference.
val termCompleter = new Completer(Mode.Term, pos, matches)
val termCompleter = new Completer(Mode.Term, pos, untpdPath, matches)
val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap:
case (name, denots) => denots.collect:
case d: SymDenotation if d.isTerm && d.termRef.symbol.is(Extension) => (d.termRef, name.asTermName)
Expand All @@ -557,14 +572,16 @@ object Completion:
}
extMethodsWithAppliedReceiver.groupByName

lazy val isNew: Boolean = isInNewContext(untpdPath)

/** Include in completion sets only symbols that
* 1. match the filter method,
* 2. satisfy [[Completion.isValidCompletionSymbol]]
*/
private def include(denot: SingleDenotation, nameInScope: Name)(using Context): Boolean =
matches(nameInScope) &&
completionsFilter(NoType, nameInScope) &&
isValidCompletionSymbol(denot.symbol, mode)
isValidCompletionSymbol(denot.symbol, mode, isNew)

private def extractRefinements(site: Type)(using Context): Seq[SingleDenotation] =
site match
Expand Down
Loading

0 comments on commit ca916f0

Please sign in to comment.