Skip to content

Commit

Permalink
Fix handling of singleton types
Browse files Browse the repository at this point in the history
 - Don't widen actual in adaptBoxed if expected is a singleton type
 - Don't allow singleton types with capture sets (the theory does not
   allow them either)
  • Loading branch information
odersky committed Jul 8, 2023
1 parent c9f7ee0 commit ffee8bd
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 18 deletions.
40 changes: 24 additions & 16 deletions compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ object CheckCaptures:
/** Check that a @retains annotation only mentions references that can be tracked.
* This check is performed at Typer.
*/
def checkWellformed(ann: Tree)(using Context): Unit =
def checkWellformed(parent: Tree, ann: Tree)(using Context): Unit =
parent.tpe match
case _: SingletonType =>
report.error(em"Singleton type $parent cannot have capture set", parent.srcPos)
case _ =>
for elem <- retainedElems(ann) do
elem.tpe match
case ref: CaptureRef =>
Expand Down Expand Up @@ -848,21 +852,25 @@ class CheckCaptures extends Recheck, SymTransformer:
adaptedType(boxed)
}

var actualw = actual.widenDealias
actual match
case ref: CaptureRef if ref.isTracked =>
actualw match
case CapturingType(p, refs) if ref.singletonCaptureSet.mightSubcapture(refs) =>
actualw = actualw.derivedCapturingType(p, ref.singletonCaptureSet)
.showing(i"improve $actualw to $result", capt)
// given `a: C T`, improve `C T` to `{a} T`
case _ =>
case _ =>
val adapted = adapt(actualw, expected, covariant = true)
if adapted ne actualw then
capt.println(i"adapt boxed $actual vs $expected ===> $adapted")
adapted
else actual
if expected.isSingleton && actual.isSingleton then
println(i"shot $actual $expected")
actual
else
var actualw = actual.widenDealias
actual match
case ref: CaptureRef if ref.isTracked =>
actualw match
case CapturingType(p, refs) if ref.singletonCaptureSet.mightSubcapture(refs) =>
actualw = actualw.derivedCapturingType(p, ref.singletonCaptureSet)
.showing(i"improve $actualw to $result", capt)
// given `a: T^C`, improve `T^C` to `T^{a}`
case _ =>
case _ =>
val adapted = adapt(actualw, expected, covariant = true)
if adapted ne actualw then
capt.println(i"adapt boxed $actual vs $expected ===> $adapted")
adapted
else actual
end adaptBoxed

/** Check overrides again, taking capture sets into account.
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2897,7 +2897,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
if Feature.ccEnabled
&& (cls == defn.RetainsAnnot || cls == defn.RetainsByNameAnnot)
then
CheckCaptures.checkWellformed(annot1)
CheckCaptures.checkWellformed(arg1, annot1)
if arg1.isType then
assignType(cpy.Annotated(tree)(arg1, annot1), arg1, annot1)
else
Expand Down Expand Up @@ -3957,7 +3957,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
sym.isConstructor
|| sym.matchNullaryLoosely
|| Feature.warnOnMigration(msg, tree.srcPos, version = `3.0`)
&& {
&& {
msg.actions
.headOption
.foreach(Rewrites.applyAction)
Expand Down
6 changes: 6 additions & 0 deletions tests/neg-custom-args/captures/singletons.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
val x = () => ()

val y1: x.type = x // ok
val y2: x.type^{} = x // error: singleton type cannot have capture set
val y3: x.type^{x} = x // error: singleton type cannot have capture set // error
val y4: x.type^{cap} = x // error: singleton type cannot have capture set

0 comments on commit ffee8bd

Please sign in to comment.