Skip to content

Commit

Permalink
Merge branch 'master' into fix/18645-overloaded-ext-method-compile-er…
Browse files Browse the repository at this point in the history
…ror-with-braces
  • Loading branch information
i10416 committed Feb 9, 2024
2 parents 5cf6707 + 067ec20 commit 5a41b9c
Show file tree
Hide file tree
Showing 118 changed files with 1,697 additions and 1,119 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ jobs:
run: ./project/scripts/sbt ";set ThisBuild/Build.scala2Library := Build.Scala2LibraryTasty ;scala3-bootstrapped/testCompilation i5; scala3-bootstrapped/testCompilation tests/run/typelevel-peano.scala; scala3-bootstrapped/testOnly dotty.tools.backend.jvm.DottyBytecodeTests" # only test a subset of test to avoid doubling the CI execution time

- name: Test with Scala 2 library with CC TASTy (fast)
run: ./project/scripts/sbt "scala2-library-cc/compile; scala2-library-cc-tasty/compile" # TODO test all the test configurations in non-CC library (currently disabled due to bug while loading the library)
run: ./project/scripts/sbt "scala2-library-cc/compile; scala2-library-cc-tasty/compile; scala3-bootstrapped/testCompilation i3"

test_scala2_library_tasty:
runs-on: [self-hosted, Linux]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ final case class SbtCommunityProject(
case Some(ivyHome) => List(s"-Dsbt.ivy.home=$ivyHome")
case _ => Nil
extraSbtArgs ++ sbtProps ++ List(
"-sbt-version", "1.9.3",
"-sbt-version", "1.9.7",
"-Dsbt.supershell=false",
s"-Ddotty.communitybuild.dir=$communitybuildDir",
s"--addPluginSbtFile=$sbtPluginFilePath"
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -578,8 +578,9 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
sym.owner.isPrimitiveValueClass
|| sym.owner == defn.StringClass
|| defn.pureMethods.contains(sym)

tree.tpe.isInstanceOf[ConstantType] && tree.symbol != NoSymbol && isKnownPureOp(tree.symbol) // A constant expression with pure arguments is pure.
|| fn.symbol.isStableMember && !fn.symbol.is(Lazy) // constructors of no-inits classes are stable
|| fn.symbol.isStableMember && fn.symbol.isConstructor // constructors of no-inits classes are stable

/** The purity level of this reference.
* @return
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
* otherwise specified).
*/
def Closure(meth: TermSymbol, rhsFn: List[List[Tree]] => Tree, targs: List[Tree] = Nil, targetType: Type = NoType)(using Context): Block = {
val targetTpt = if (targetType.exists) TypeTree(targetType) else EmptyTree
val targetTpt = if (targetType.exists) TypeTree(targetType, inferred = true) else EmptyTree
val call =
if (targs.isEmpty) Ident(TermRef(NoPrefix, meth))
else TypeApply(Ident(TermRef(NoPrefix, meth)), targs)
Block(
DefDef(meth, rhsFn) :: Nil,
Closure(Nil, call, targetTpt))
var mdef0 = DefDef(meth, rhsFn)
val mdef = cpy.DefDef(mdef0)(tpt = TypeTree(mdef0.tpt.tpe, inferred = true))
Block(mdef :: Nil, Closure(Nil, call, targetTpt))
}

/** A closure whose anonymous function has the given method type */
Expand Down
67 changes: 55 additions & 12 deletions compiler/src/dotty/tools/dotc/cc/CaptureOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,14 @@ extension (tree: Tree)
tree.putAttachment(Captures, refs)
refs

/** The arguments of a @retains or @retainsByName annotation */
/** The arguments of a @retains, @retainsCap or @retainsByName annotation */
def retainedElems(using Context): List[Tree] = tree match
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) => elems
case _ => Nil
case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) =>
elems
case _ =>
if tree.symbol.maybeOwner == defn.RetainsCapAnnot
then ref(defn.captureRoot.termRef) :: Nil
else Nil

extension (tp: Type)

Expand Down Expand Up @@ -207,7 +211,7 @@ extension (tp: Type)
def dropAllRetains(using Context): Type = // TODO we should drop retains from inferred types before unpickling
val tm = new TypeMap:
def apply(t: Type) = t match
case AnnotatedType(parent, annot) if annot.symbol == defn.RetainsAnnot =>
case AnnotatedType(parent, annot) if annot.symbol.isRetains =>
apply(parent)
case _ =>
mapOver(t)
Expand All @@ -220,9 +224,31 @@ extension (tp: Type)
* type of `x`. If `x` and `y` are different variables then `{x*}` and `{y*}`
* are unrelated.
*/
def reach(using Context): CaptureRef =
assert(tp.isTrackableRef)
AnnotatedType(tp, Annotation(defn.ReachCapabilityAnnot, util.Spans.NoSpan))
def reach(using Context): CaptureRef = tp match
case tp: CaptureRef if tp.isTrackableRef =>
if tp.isReach then tp else ReachCapability(tp)

/** If `x` is a capture ref, its maybe capability `x?`, represented internally
* as `x @maybeCapability`. `x?` stands for a capability `x` that might or might
* not be part of a capture set. We have `{} <: {x?} <: {x}`. Maybe capabilities
* cannot be propagated between sets. If `a <: b` and `a` acquires `x?` then
* `x` is propagated to `b` as a conservative approximation.
*
* Maybe capabilities should only arise for capture sets that appear in invariant
* position in their surrounding type. They are similar to TypeBunds types, but
* restricted to capture sets. For instance,
*
* Array[C^{x?}]
*
* should be morally equivalent to
*
* Array[_ >: C^{} <: C^{x}]
*
* but it has fewer issues with type inference.
*/
def maybe(using Context): CaptureRef = tp match
case tp: CaptureRef if tp.isTrackableRef =>
if tp.isMaybe then tp else MaybeCapability(tp)

/** If `ref` is a trackable capture ref, and `tp` has only covariant occurrences of a
* universal capture set, replace all these occurrences by `{ref*}`. This implements
Expand Down Expand Up @@ -326,6 +352,14 @@ extension (cls: ClassSymbol)

extension (sym: Symbol)

/** This symbol is one of `retains` or `retainsCap` */
def isRetains(using Context): Boolean =
sym == defn.RetainsAnnot || sym == defn.RetainsCapAnnot

/** This symbol is one of `retains`, `retainsCap`, or`retainsByName` */
def isRetainsLike(using Context): Boolean =
isRetains || sym == defn.RetainsByNameAnnot

/** A class is pure if:
* - one its base types has an explicitly declared self type with an empty capture set
* - or it is a value class
Expand Down Expand Up @@ -419,12 +453,21 @@ object ReachCapabilityApply:
case Apply(reach, arg :: Nil) if reach.symbol == defn.Caps_reachCapability => Some(arg)
case _ => None

class AnnotatedCapability(annot: Context ?=> ClassSymbol):
def apply(tp: Type)(using Context) =
AnnotatedType(tp, Annotation(annot, util.Spans.NoSpan))
def unapply(tree: AnnotatedType)(using Context): Option[SingletonCaptureRef] = tree match
case AnnotatedType(parent: SingletonCaptureRef, ann) if ann.symbol == annot => Some(parent)
case _ => None

/** An extractor for `ref @annotation.internal.reachCapability`, which is used to express
* the reach capability `ref*` as a type.
*/
object ReachCapability:
def unapply(tree: AnnotatedType)(using Context): Option[SingletonCaptureRef] = tree match
case AnnotatedType(parent: SingletonCaptureRef, ann)
if ann.symbol == defn.ReachCapabilityAnnot => Some(parent)
case _ => None
object ReachCapability extends AnnotatedCapability(defn.ReachCapabilityAnnot)

/** An extractor for `ref @maybeCapability`, which is used to express
* the maybe capability `ref?` as a type.
*/
object MaybeCapability extends AnnotatedCapability(defn.MaybeCapabilityAnnot)


40 changes: 34 additions & 6 deletions compiler/src/dotty/tools/dotc/cc/CaptureSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,16 @@ sealed abstract class CaptureSet extends Showable:

/** x subsumes x
* this subsumes this.f
* x subsumes y ==> x* subsumes y
* x subsumes y ==> x* subsumes y*
* x subsumes y ==> x* subsumes y, x subsumes y?
* x subsumes y ==> x* subsumes y*, x? subsumes y?
*/
extension (x: CaptureRef)
private def subsumes(y: CaptureRef)(using Context): Boolean =
(x eq y)
|| x.isRootCapability
|| y.match
case y: TermRef => !y.isReach && (y.prefix eq x)
case y: TermRef => y.prefix eq x
case MaybeCapability(y1) => x.stripMaybe.subsumes(y1)
case _ => false
|| x.match
case ReachCapability(x1) => x1.subsumes(y.stripReach)
Expand Down Expand Up @@ -312,6 +313,8 @@ sealed abstract class CaptureSet extends Showable:
def substParams(tl: BindingType, to: List[Type])(using Context) =
map(Substituters.SubstParamsMap(tl, to))

def maybe(using Context): CaptureSet = map(MaybeMap())

/** Invoke handler if this set has (or later aquires) the root capability `cap` */
def disallowRootCapability(handler: () => Context ?=> Unit)(using Context): this.type =
if isUniversal then handler()
Expand Down Expand Up @@ -445,6 +448,8 @@ object CaptureSet:
def isConst = isSolved
def isAlwaysEmpty = false

def isMaybeSet = false // overridden in BiMapped

/** A handler to be invoked if the root reference `cap` is added to this set */
var rootAddedHandler: () => Context ?=> Unit = () => ()

Expand Down Expand Up @@ -490,9 +495,10 @@ object CaptureSet:
if elem.isRootCapability then
rootAddedHandler()
newElemAddedHandler(elem)
val normElem = if isMaybeSet then elem else elem.stripMaybe
// assert(id != 5 || elems.size != 3, this)
val res = (CompareResult.OK /: deps): (r, dep) =>
r.andAlso(dep.tryInclude(elem, this))
r.andAlso(dep.tryInclude(normElem, this))
res.orElse:
elems -= elem
res.addToTrace(this)
Expand All @@ -508,6 +514,8 @@ object CaptureSet:
levelLimit.isContainedIn(elem.cls.levelOwner)
case ReachCapability(elem1) =>
levelOK(elem1)
case MaybeCapability(elem1) =>
levelOK(elem1)
case _ =>
true

Expand Down Expand Up @@ -760,6 +768,7 @@ object CaptureSet:
if source eq origin then supApprox.map(bimap.inverse)
else source.upperApprox(this).map(bimap) ** supApprox

override def isMaybeSet: Boolean = bimap.isInstanceOf[MaybeMap]
override def toString = s"BiMapped$id($source, elems = $elems)"
end BiMapped

Expand Down Expand Up @@ -840,8 +849,7 @@ object CaptureSet:
upper.isAlwaysEmpty || upper.isConst && upper.elems.size == 1 && upper.elems.contains(r1)
if variance > 0 || isExact then upper
else if variance < 0 then CaptureSet.empty
else if ctx.mode.is(Mode.Printing) then upper
else assert(false, i"trying to add $upper from $r via ${tm.getClass} in a non-variant setting")
else upper.maybe

/** Apply `f` to each element in `xs`, and join result sets with `++` */
def mapRefs(xs: Refs, f: CaptureRef => CaptureSet)(using Context): CaptureSet =
Expand Down Expand Up @@ -980,6 +988,26 @@ object CaptureSet:
/** The current VarState, as passed by the implicit context */
def varState(using state: VarState): VarState = state

/** Maps `x` to `x?` */
private class MaybeMap(using Context) extends BiTypeMap:

def apply(t: Type) = t match
case t: CaptureRef if t.isTrackableRef => t.maybe
case _ => mapOver(t)

override def toString = "Maybe"

lazy val inverse = new BiTypeMap:

def apply(t: Type) = t match
case t: CaptureRef if t.isMaybe => t.stripMaybe
case t => mapOver(t)

def inverse = MaybeMap.this

override def toString = "Maybe.inverse"
end MaybeMap

/* Not needed:
def ofClass(cinfo: ClassInfo, argTypes: List[Type])(using Context): CaptureSet =
CaptureSet.empty
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/cc/CapturingType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ object CapturingType:
case AnnotatedType(parent, ann: CaptureAnnotation)
if isCaptureCheckingOrSetup =>
Some((parent, ann.refs))
case AnnotatedType(parent, ann)
if ann.symbol == defn.RetainsAnnot && isCaptureChecking =>
case AnnotatedType(parent, ann) if ann.symbol.isRetains && isCaptureChecking =>
// There are some circumstances where we cannot map annotated types
// with retains annotations to capturing types, so this second recognizer
// path still has to exist. One example is when checking capture sets
Expand Down
6 changes: 0 additions & 6 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ object CheckCaptures:
*/
final class SubstParamsMap(from: BindingType, to: List[Type])(using Context)
extends ApproximatingTypeMap, IdempotentCaptRefMap:
/** This SubstParamsMap is exact if `to` only contains `CaptureRef`s. */
private val isExactSubstitution: Boolean = to.forall(_.isTrackableRef)

/** As long as this substitution is exact, there is no need to create `Range`s when mapping invariant positions. */
override protected def needsRangeIfInvariant(refs: CaptureSet): Boolean = !isExactSubstitution

def apply(tp: Type): Type =
tp match
case tp: ParamRef =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/cc/RetainingType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object RetainingType:

def unapply(tp: AnnotatedType)(using Context): Option[(Type, List[Tree])] =
val sym = tp.annot.symbol
if sym == defn.RetainsAnnot || sym == defn.RetainsByNameAnnot then
if sym.isRetainsLike then
tp.annot match
case _: CaptureAnnotation =>
assert(ctx.mode.is(Mode.IgnoreCaptures), s"bad retains $tp at ${ctx.phase}")
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/cc/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
case tp @ CapturingType(parent, refs) =>
if tp.isBoxed then tp else tp.boxed
case tp @ AnnotatedType(parent, ann) =>
if ann.symbol == defn.RetainsAnnot
if ann.symbol.isRetains
then CapturingType(parent, ann.tree.toCaptureSet, boxed = true)
else tp.derivedAnnotatedType(box(parent), ann)
case tp1 @ AppliedType(tycon, args) if defn.isNonRefinedFunction(tp1) =>
Expand Down Expand Up @@ -192,7 +192,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:

def apply(tp: Type) =
val tp1 = tp match
case AnnotatedType(parent, annot) if annot.symbol == defn.RetainsAnnot =>
case AnnotatedType(parent, annot) if annot.symbol.isRetains =>
// Drop explicit retains annotations
apply(parent)
case tp @ AppliedType(tycon, args) =>
Expand Down Expand Up @@ -283,7 +283,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
t.derivedCapturingType(this(parent), refs)
case t @ AnnotatedType(parent, ann) =>
val parent1 = this(parent)
if ann.symbol == defn.RetainsAnnot then
if ann.symbol.isRetains then
for tpt <- tptToCheck do
checkWellformedLater(parent1, ann.tree, tpt)
CapturingType(parent1, ann.tree.toCaptureSet)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ object Contexts {
inline def atPhaseNoEarlier[T](limit: Phase)(inline op: Context ?=> T)(using Context): T =
op(using if !limit.exists || limit <= ctx.phase then ctx else ctx.withPhase(limit))

inline def inMode[T](mode: Mode)(inline op: Context ?=> T)(using ctx: Context): T =
inline private def inMode[T](mode: Mode)(inline op: Context ?=> T)(using ctx: Context): T =
op(using if mode != ctx.mode then ctx.fresh.setMode(mode) else ctx)

inline def withMode[T](mode: Mode)(inline op: Context ?=> T)(using ctx: Context): T =
Expand Down
25 changes: 15 additions & 10 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,10 @@ class Definitions {
List(AnyType), EmptyScope)
@tu lazy val SingletonType: TypeRef = SingletonClass.typeRef

@tu lazy val MaybeCapabilityAnnot: ClassSymbol =
completeClass(enterCompleteClassSymbol(
ScalaPackageClass, tpnme.maybeCapability, Final, List(StaticAnnotationClass.typeRef)))

@tu lazy val CollectionSeqType: TypeRef = requiredClassRef("scala.collection.Seq")
@tu lazy val SeqType: TypeRef = requiredClassRef("scala.collection.immutable.Seq")
@tu lazy val SeqModule: Symbol = requiredModule("scala.collection.immutable.Seq")
Expand Down Expand Up @@ -993,7 +997,7 @@ class Definitions {

// Annotation base classes
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")
// @tu lazy val StaticAnnotationClass: ClassSymbol = requiredClass("scala.annotation.StaticAnnotation")
@tu lazy val StaticAnnotationClass: ClassSymbol = requiredClass("scala.annotation.StaticAnnotation")
@tu lazy val RefiningAnnotationClass: ClassSymbol = requiredClass("scala.annotation.RefiningAnnotation")
@tu lazy val JavaAnnotationClass: ClassSymbol = requiredClass("java.lang.annotation.Annotation")

Expand Down Expand Up @@ -1057,6 +1061,7 @@ class Definitions {
@tu lazy val ReachCapabilityAnnot = requiredClass("scala.annotation.internal.reachCapability")
@tu lazy val RequiresCapabilityAnnot: ClassSymbol = requiredClass("scala.annotation.internal.requiresCapability")
@tu lazy val RetainsAnnot: ClassSymbol = requiredClass("scala.annotation.retains")
@tu lazy val RetainsCapAnnot: ClassSymbol = requiredClass("scala.annotation.retainsCap")
@tu lazy val RetainsByNameAnnot: ClassSymbol = requiredClass("scala.annotation.retainsByName")
@tu lazy val PublicInBinaryAnnot: ClassSymbol = requiredClass("scala.annotation.publicInBinary")

Expand Down Expand Up @@ -1171,19 +1176,18 @@ class Definitions {
}
}

object RefinedFunctionOf {
object RefinedFunctionOf:

/** Matches a refined `PolyFunction`/`FunctionN[...]`/`ContextFunctionN[...]`.
* Extracts the method type type and apply info.
*/
def unapply(tpe: RefinedType)(using Context): Option[MethodOrPoly] = {
def unapply(tpe: RefinedType)(using Context): Option[MethodOrPoly] =
tpe.refinedInfo match
case mt: MethodOrPoly
if tpe.refinedName == nme.apply
&& (tpe.parent.derivesFrom(defn.PolyFunctionClass) || isFunctionNType(tpe.parent)) =>
Some(mt)
if tpe.refinedName == nme.apply && isFunctionType(tpe.parent) => Some(mt)
case _ => None
}
}

end RefinedFunctionOf

object PolyFunctionOf {

Expand Down Expand Up @@ -2008,7 +2012,7 @@ class Definitions {
@tu lazy val ccExperimental: Set[Symbol] = Set(
CapsModule, CapsModule.moduleClass, PureClass,
CapabilityAnnot, RequiresCapabilityAnnot,
RetainsAnnot, RetainsByNameAnnot)
RetainsAnnot, RetainsCapAnnot, RetainsByNameAnnot)

// ----- primitive value class machinery ------------------------------------------

Expand Down Expand Up @@ -2137,7 +2141,8 @@ class Definitions {
AnyValClass,
NullClass,
NothingClass,
SingletonClass)
SingletonClass,
MaybeCapabilityAnnot)

@tu lazy val syntheticCoreClasses: List[Symbol] = syntheticScalaClasses ++ List(
EmptyPackageVal,
Expand Down
Loading

0 comments on commit 5a41b9c

Please sign in to comment.