Skip to content

Commit

Permalink
Merge branch 'main' into export-diagnostics
Browse files Browse the repository at this point in the history
  • Loading branch information
tanishiking committed Jul 27, 2023
2 parents 4f6a092 + 182331b commit a6dfec2
Show file tree
Hide file tree
Showing 174 changed files with 13,391 additions and 1,041 deletions.
4 changes: 2 additions & 2 deletions community-build/src/scala/dotty/communitybuild/projects.scala
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ object projects:
sbtTestCommand = """set deriving/scalacOptions -= "-Xfatal-warnings"; set typeable/scalacOptions -= "-Xfatal-warnings"; test""",
// selectively disable -Xfatal-warnings due to deprecations
sbtDocCommand = forceDoc("typeable", "deriving", "data"),
scalacOptions = Nil // disable -Ysafe-init, due to -Xfatal-warnings
scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Ysafe-init"), // due to -Xfatal-warnings
)

lazy val xmlInterpolator = SbtCommunityProject(
Expand Down Expand Up @@ -682,7 +682,7 @@ object projects:
sbtTestCommand = "runCommunityBuild",
sbtPublishCommand = "publishLocal",
dependencies = List(scalatest),
scalacOptions = List("-language:implicitConversions"), // disabled -Ysafe-init, due to bug in macro
scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Xcheck-macros") :+ "-language:implicitConversions", // disabled -Xcheck-macros, due to bug in macro
)

lazy val onnxScala = SbtCommunityProject(
Expand Down
12 changes: 5 additions & 7 deletions compiler/src/dotty/tools/backend/jvm/CodeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import scala.tools.asm
import scala.tools.asm.tree._
import tpd._
import dotty.tools.io.AbstractFile
import dotty.tools.dotc.util
import dotty.tools.dotc.util.NoSourcePosition


Expand Down Expand Up @@ -106,7 +107,7 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
}

// Creates a callback that will be evaluated in PostProcessor after creating a file
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: interfaces.SourceFile): AbstractFile => Unit = clsFile => {
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: util.SourceFile): AbstractFile => Unit = clsFile => {
val (fullClassName, isLocal) = atPhase(sbtExtractDependenciesPhase) {
(ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal)
}
Expand All @@ -115,12 +116,9 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
if (ctx.compilerCallback != null)
ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className)

if (ctx.sbtCallback != null) {
val jSourceFile = sourceFile.jfile.orElse(null)
val cb = ctx.sbtCallback
if (isLocal) cb.generatedLocalClass(jSourceFile, clsFile.file)
else cb.generatedNonLocalClass(jSourceFile, clsFile.file, className, fullClassName)
}
ctx.withIncCallback: cb =>
if (isLocal) cb.generatedLocalClass(sourceFile, clsFile.jpath)
else cb.generatedNonLocalClass(sourceFile, clsFile.jpath, className, fullClassName)
}

/** Convert a `dotty.tools.io.AbstractFile` into a
Expand Down
65 changes: 42 additions & 23 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import util.Spans._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags
import Symbols._, StdNames._, Trees._, ContextOps._
import Decorators._, transform.SymUtils._
import Annotations.Annotation
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName, WildcardParamName}
import NameKinds.{UniqueName, ContextBoundParamName, ContextFunctionParamName, DefaultGetterName, WildcardParamName}
import typer.{Namer, Checking}
import util.{Property, SourceFile, SourcePosition, Chars}
import config.Feature.{sourceVersion, migrateTo3, enabled}
Expand Down Expand Up @@ -203,10 +203,14 @@ object desugar {
else vdef1
end valDef

def makeImplicitParameters(tpts: List[Tree], implicitFlag: FlagSet, forPrimaryConstructor: Boolean = false)(using Context): List[ValDef] =
for (tpt <- tpts) yield {
def makeImplicitParameters(
tpts: List[Tree], implicitFlag: FlagSet,
mkParamName: () => TermName,
forPrimaryConstructor: Boolean = false
)(using Context): List[ValDef] =
for (tpt, i) <- tpts.zipWithIndex yield {
val paramFlags: FlagSet = if (forPrimaryConstructor) LocalParamAccessor else Param
val epname = EvidenceParamName.fresh()
val epname = mkParamName()
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | implicitFlag)
}

Expand Down Expand Up @@ -240,17 +244,27 @@ object desugar {
val DefDef(_, paramss, tpt, rhs) = meth
val evidenceParamBuf = ListBuffer[ValDef]()

var seenContextBounds: Int = 0
def desugarContextBounds(rhs: Tree): Tree = rhs match
case ContextBounds(tbounds, cxbounds) =>
val iflag = if sourceVersion.isAtLeast(`future`) then Given else Implicit
evidenceParamBuf ++= makeImplicitParameters(
cxbounds, iflag, forPrimaryConstructor = isPrimaryConstructor)
cxbounds, iflag,
// Just like with `makeSyntheticParameter` on nameless parameters of
// using clauses, we only need names that are unique among the
// parameters of the method since shadowing does not affect
// implicit resolution in Scala 3.
mkParamName = () =>
val index = seenContextBounds + 1 // Start at 1 like FreshNameCreator.
val ret = ContextBoundParamName(EmptyTermName, index)
seenContextBounds += 1
ret,
forPrimaryConstructor = isPrimaryConstructor)
tbounds
case LambdaTypeTree(tparams, body) =>
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
case _ =>
rhs

val paramssNoContextBounds =
mapParamss(paramss) {
tparam => cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
Expand Down Expand Up @@ -381,11 +395,14 @@ object desugar {
tree match
case untpd.Block(stats, expr) =>
val (untpdTypeVariables, otherStats) = stats.span {
case tdef @ untpd.TypeDef(name, _) => name.isVarPattern
case tdef @ untpd.TypeDef(name, _) => !tdef.isBackquoted && name.isVarPattern
case _ => false
}
val untpdCaseTypeVariables = untpdTypeVariables.asInstanceOf[List[untpd.TypeDef]].map {
tdef => tdef.withMods(tdef.mods | Case)
}
val pattern = if otherStats.isEmpty then expr else untpd.cpy.Block(tree)(otherStats, expr)
(untpdTypeVariables.asInstanceOf[List[untpd.TypeDef]], pattern)
(untpdCaseTypeVariables, pattern)
case _ =>
(Nil, tree)

Expand All @@ -406,11 +423,11 @@ object desugar {
meth.paramss :+ evidenceParams
cpy.DefDef(meth)(paramss = paramss1)

/** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */
/** The parameters generated from the contextual bounds of `meth`, as generated by `desugar.defDef` */
private def evidenceParams(meth: DefDef)(using Context): List[ValDef] =
meth.paramss.reverse match {
case ValDefs(vparams @ (vparam :: _)) :: _ if vparam.mods.isOneOf(GivenOrImplicit) =>
vparams.takeWhile(_.name.is(EvidenceParamName))
vparams.takeWhile(_.name.is(ContextBoundParamName))
case _ =>
Nil
}
Expand Down Expand Up @@ -1014,7 +1031,7 @@ object desugar {
* if the type has a pattern variable name
*/
def quotedPatternTypeDef(tree: TypeDef)(using Context): TypeDef = {
assert(ctx.mode.is(Mode.QuotedPattern))
assert(ctx.mode.isQuotedPattern)
if tree.name.isVarPattern && !tree.isBackquoted then
val patternTypeAnnot = New(ref(defn.QuotedRuntimePatterns_patternTypeAnnot.typeRef)).withSpan(tree.span)
val mods = tree.mods.withAddedAnnotation(patternTypeAnnot)
Expand Down Expand Up @@ -1083,19 +1100,21 @@ object desugar {
*/
def makePolyFunctionType(tree: PolyFunction)(using Context): RefinedTypeTree =
val PolyFunction(tparams: List[untpd.TypeDef] @unchecked, fun @ untpd.Function(vparamTypes, res)) = tree: @unchecked
val funFlags = fun match
val paramFlags = fun match
case fun: FunctionWithMods =>
fun.mods.flags
case _ => EmptyFlags
// TODO: make use of this in the desugaring when pureFuns is enabled.
// val isImpure = funFlags.is(Impure)

// TODO: make use of this in the desugaring when pureFuns is enabled.
// val isImpure = funFlags.is(Impure)
// Function flags to be propagated to each parameter in the desugared method type.
val givenFlag = fun.mods.flags.toTermFlags & Given
fun.erasedParams.map(isErased => if isErased then givenFlag | Erased else givenFlag)
case _ =>
vparamTypes.map(_ => EmptyFlags)

// Function flags to be propagated to each parameter in the desugared method type.
val paramFlags = funFlags.toTermFlags & Given
val vparams = vparamTypes.zipWithIndex.map:
case (p: ValDef, _) => p.withAddedFlags(paramFlags)
case (p, n) => makeSyntheticParameter(n + 1, p).withAddedFlags(paramFlags)
val vparams = vparamTypes.lazyZip(paramFlags).zipWithIndex.map {
case ((p: ValDef, paramFlags), n) => p.withAddedFlags(paramFlags)
case ((p, paramFlags), n) => makeSyntheticParameter(n + 1, p).withAddedFlags(paramFlags)
}.toList

RefinedTypeTree(ref(defn.PolyFunctionType), List(
DefDef(nme.apply, tparams :: vparams :: Nil, res, EmptyTree).withFlags(Synthetic)
Expand Down Expand Up @@ -1363,7 +1382,7 @@ object desugar {
case tree: ValDef => valDef(tree)
case tree: TypeDef =>
if (tree.isClassDef) classDef(tree)
else if (ctx.mode.is(Mode.QuotedPattern)) quotedPatternTypeDef(tree)
else if (ctx.mode.isQuotedPattern) quotedPatternTypeDef(tree)
else tree
case tree: DefDef =>
if (tree.name.isConstructorName) tree // was already handled by enclosing classDef
Expand Down Expand Up @@ -1585,7 +1604,7 @@ object desugar {

def makeContextualFunction(formals: List[Tree], body: Tree, erasedParams: List[Boolean])(using Context): Function = {
val mods = Given
val params = makeImplicitParameters(formals, mods)
val params = makeImplicitParameters(formals, mods, mkParamName = () => ContextFunctionParamName.fresh())
FunctionWithMods(params, body, Modifiers(mods), erasedParams)
}

Expand Down
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/cc/CaptureOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import config.Feature
private val Captures: Key[CaptureSet] = Key()
private val BoxedType: Key[BoxedTypeCache] = Key()

/** Switch whether unpickled function types and byname types should be mapped to
* impure types. With the new gradual typing using Fluid capture sets, this should
* be no longer needed. Also, it has bad interactions with pickling tests.
*/
private val adaptUnpickledFunctionTypes = false

/** The arguments of a @retains or @retainsByName annotation */
private[cc] def retainedElems(tree: Tree)(using Context): List[Tree] = tree match
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) => elems
Expand Down Expand Up @@ -49,7 +55,7 @@ extension (tree: Tree)
* a by name parameter type, turning the latter into an impure by name parameter type.
*/
def adaptByNameArgUnderPureFuns(using Context): Tree =
if Feature.pureFunsEnabledSomewhere then
if adaptUnpickledFunctionTypes && Feature.pureFunsEnabledSomewhere then
val rbn = defn.RetainsByNameAnnot
Annotated(tree,
New(rbn.typeRef).select(rbn.primaryConstructor).appliedTo(
Expand Down Expand Up @@ -145,7 +151,7 @@ extension (tp: Type)
*/
def adaptFunctionTypeUnderPureFuns(using Context): Type = tp match
case AppliedType(fn, args)
if Feature.pureFunsEnabledSomewhere && defn.isFunctionClass(fn.typeSymbol) =>
if adaptUnpickledFunctionTypes && Feature.pureFunsEnabledSomewhere && defn.isFunctionClass(fn.typeSymbol) =>
val fname = fn.typeSymbol.name
defn.FunctionType(
fname.functionArity,
Expand Down
16 changes: 15 additions & 1 deletion compiler/src/dotty/tools/dotc/cc/CaptureSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,20 @@ object CaptureSet:
override def toString = elems.toString
end Const

/** A special capture set that gets added to the types of symbols that were not
* themselves capture checked, in order to admit arbitrary corresponding capture
* sets in subcapturing comparisons. Similar to platform types for explicit
* nulls, this provides more lenient checking against compilation units that
* were not yet compiled with capture checking on.
*/
object Fluid extends Const(emptySet):
override def isAlwaysEmpty = false
override def addNewElems(elems: Refs, origin: CaptureSet)(using Context, VarState) = CompareResult.OK
override def accountsFor(x: CaptureRef)(using Context): Boolean = true
override def mightAccountFor(x: CaptureRef)(using Context): Boolean = true
override def toString = "<fluid>"
end Fluid

/** The subclass of captureset variables with given initial elements */
class Var(initialElems: Refs = emptySet) extends CaptureSet:

Expand Down Expand Up @@ -863,7 +877,7 @@ object CaptureSet:
case CapturingType(parent, refs) =>
recur(parent) ++ refs
case tpd @ RefinedType(parent, _, rinfo: MethodType)
if followResult && defn.isFunctionType(tpd) =>
if followResult && defn.isFunctionNType(tpd) =>
ofType(parent, followResult = false) // pick up capture set from parent type
++ (recur(rinfo.resType) // add capture set of result
-- CaptureSet(rinfo.paramRefs.filter(_.isTracked)*)) // but disregard bound parameters
Expand Down
61 changes: 54 additions & 7 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,46 @@ class CheckCaptures extends Recheck, SymTransformer:
def includeCallCaptures(sym: Symbol, pos: SrcPos)(using Context): Unit =
if sym.exists && curEnv.isOpen then markFree(capturedVars(sym), pos)

private def handleBackwardsCompat(tp: Type, sym: Symbol, initialVariance: Int = 1)(using Context): Type =
val fluidify = new TypeMap with IdempotentCaptRefMap:
variance = initialVariance
def apply(t: Type): Type = t match
case t: MethodType =>
mapOver(t)
case t: TypeLambda =>
t.derivedLambdaType(resType = this(t.resType))
case CapturingType(_, _) =>
t
case _ =>
val t1 = t match
case t @ RefinedType(parent, rname, rinfo: MethodType) if defn.isFunctionType(t) =>
t.derivedRefinedType(parent, rname, this(rinfo))
case _ =>
mapOver(t)
if variance > 0 then t1
else Setup.decorate(t1, Function.const(CaptureSet.Fluid))

def isPreCC(sym: Symbol): Boolean =
sym.isTerm && sym.maybeOwner.isClass
&& !sym.owner.is(CaptureChecked)
&& !defn.isFunctionSymbol(sym.owner)

if isPreCC(sym) then
val tpw = tp.widen
val fluidTp = fluidify(tpw)
if fluidTp eq tpw then tp
else fluidTp.showing(i"fluid for ${sym.showLocated}, ${sym.is(JavaDefined)}: $tp --> $result", capt)
else tp
end handleBackwardsCompat

override def recheckIdent(tree: Ident)(using Context): Type =
if tree.symbol.is(Method) then
if tree.symbol.info.isParameterless then
// there won't be an apply; need to include call captures now
includeCallCaptures(tree.symbol, tree.srcPos)
else
markFree(tree.symbol, tree.srcPos)
super.recheckIdent(tree)
handleBackwardsCompat(super.recheckIdent(tree), tree.symbol)

/** A specialized implementation of the selection rule.
*
Expand Down Expand Up @@ -327,7 +359,7 @@ class CheckCaptures extends Recheck, SymTransformer:
val selType = recheckSelection(tree, qualType, name, disambiguate)
val selCs = selType.widen.captureSet
if selCs.isAlwaysEmpty || selType.widen.isBoxedCapturing || qualType.isBoxedCapturing then
selType
handleBackwardsCompat(selType, tree.symbol)
else
val qualCs = qualType.captureSet
capt.println(i"pick one of $qualType, ${selType.widen}, $qualCs, $selCs in $tree")
Expand Down Expand Up @@ -362,7 +394,16 @@ class CheckCaptures extends Recheck, SymTransformer:
val argType0 = f(recheckStart(arg, pt))
val argType = super.recheckFinish(argType0, arg, pt)
super.recheckFinish(argType, tree, pt)
if meth == defn.Caps_unsafeBox then

if meth == defn.Caps_unsafeAssumePure then
val arg :: Nil = tree.args: @unchecked
val argType0 = recheck(arg, pt.capturing(CaptureSet.universal))
val argType =
if argType0.captureSet.isAlwaysEmpty then argType0
else argType0.widen.stripCapturing
capt.println(i"rechecking $arg with ${pt.capturing(CaptureSet.universal)}: $argType")
super.recheckFinish(argType, tree, pt)
else if meth == defn.Caps_unsafeBox then
mapArgUsing(_.forceBoxStatus(true))
else if meth == defn.Caps_unsafeUnbox then
mapArgUsing(_.forceBoxStatus(false))
Expand Down Expand Up @@ -662,8 +703,10 @@ class CheckCaptures extends Recheck, SymTransformer:
/** Turn `expected` into a dependent function when `actual` is dependent. */
private def alignDependentFunction(expected: Type, actual: Type)(using Context): Type =
def recur(expected: Type): Type = expected.dealias match
case expected @ CapturingType(eparent, refs) =>
CapturingType(recur(eparent), refs, boxed = expected.isBoxed)
case expected0 @ CapturingType(eparent, refs) =>
val eparent1 = recur(eparent)
if eparent1 eq eparent then expected
else CapturingType(eparent1, refs, boxed = expected0.isBoxed)
case expected @ defn.FunctionOf(args, resultType, isContextual)
if defn.isNonRefinedFunction(expected) && defn.isFunctionNType(actual) && !defn.isNonRefinedFunction(actual) =>
val expected1 = toDepFun(args, resultType, isContextual)
Expand Down Expand Up @@ -883,7 +926,7 @@ class CheckCaptures extends Recheck, SymTransformer:
* But maybe we can then elide the check during the RefChecks phase under captureChecking?
*/
def checkOverrides = new TreeTraverser:
class OverridingPairsCheckerCC(clazz: ClassSymbol, self: Type, srcPos: SrcPos)(using Context) extends OverridingPairsChecker(clazz, self) {
class OverridingPairsCheckerCC(clazz: ClassSymbol, self: Type, srcPos: SrcPos)(using Context) extends OverridingPairsChecker(clazz, self):
/** Check subtype with box adaptation.
* This function is passed to RefChecks to check the compatibility of overriding pairs.
* @param sym symbol of the field definition that is being checked
Expand All @@ -905,7 +948,11 @@ class CheckCaptures extends Recheck, SymTransformer:
case _ => adapted
finally curEnv = saved
actual1 frozen_<:< expected1
}

override def adjustInfo(tp: Type, member: Symbol)(using Context): Type =
handleBackwardsCompat(tp, member, initialVariance = 0)
//.showing(i"adjust $other: $tp --> $result")
end OverridingPairsCheckerCC

def traverse(t: Tree)(using Context) =
t match
Expand Down
Loading

0 comments on commit a6dfec2

Please sign in to comment.