Skip to content

Commit 4942724

Browse files
committed
Revert "Implement sealed type variables"
This reverts commit 05bce38. # Conflicts: # compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala # compiler/src/dotty/tools/dotc/cc/Setup.scala # library/src/scala/caps.scala # tests/neg-custom-args/captures/capt-test.scala # tests/neg-custom-args/captures/capt1.check # tests/neg-custom-args/captures/ctest.scala # tests/neg-custom-args/captures/filevar.scala # tests/neg-custom-args/captures/heal-tparam-cs.scala # tests/neg-custom-args/captures/i15049.scala # tests/neg-custom-args/captures/i15772.check # tests/neg-custom-args/captures/i15923.scala # tests/neg-custom-args/captures/lazylists-exceptions.check # tests/neg-custom-args/captures/real-try.check # tests/neg-custom-args/captures/real-try.scala # tests/neg-custom-args/captures/sealed-leaks.scala # tests/neg-custom-args/captures/stack-alloc.scala # tests/neg-custom-args/captures/try.check # tests/neg-custom-args/captures/try.scala # tests/neg-custom-args/captures/try3.scala # tests/neg-custom-args/captures/usingLogFile.check # tests/neg-custom-args/captures/usingLogFile.scala # tests/neg-custom-args/captures/vars.check # tests/neg-custom-args/captures/vars.scala # tests/pos-custom-args/captures/vars1.scala
1 parent 95fbe8a commit 4942724

File tree

19 files changed

+63
-141
lines changed

19 files changed

+63
-141
lines changed

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

+1-22
Original file line numberDiff line numberDiff line change
@@ -173,21 +173,6 @@ object CheckCaptures:
173173
val srcTree = if ann.span.exists then ann else tpt
174174
report.warning(em"redundant capture: $remaining already accounts for $ref", srcTree.srcPos)
175175

176-
/** Report an error if some part of `tp` contains the root capability in its capture set */
177-
def disallowRootCapabilitiesIn(tp: Type, what: String, have: String, addendum: String, pos: SrcPos)(using Context) =
178-
val check = new TypeTraverser:
179-
def traverse(t: Type) =
180-
if variance >= 0 then
181-
t.captureSet.disallowRootCapability: () =>
182-
def part = if t eq tp then "" else i"the part $t of "
183-
report.error(
184-
em"""$what cannot $have $tp since
185-
|${part}that type captures the root capability `cap`.
186-
|$addendum""",
187-
pos)
188-
traverseChildren(t)
189-
check.traverse(tp)
190-
191176
/** Attachment key for bodies of closures, provided they are values */
192177
val ClosureBodyValue = Property.Key[Unit]
193178

@@ -693,17 +678,11 @@ class CheckCaptures extends Recheck, SymTransformer:
693678
val tryOwner = ccState.tryBlockOwner.remove(tree).getOrElse(ctx.owner)
694679
val saved = curEnv
695680
curEnv = Env(tryOwner, EnvKind.Regular, CaptureSet.Var(curEnv.owner), curEnv)
696-
val tp = try
681+
try
697682
inContext(ctx.withOwner(tryOwner)):
698683
super.recheckTry(tree, pt)
699684
finally
700685
curEnv = saved
701-
if allowUniversalInBoxed && Feature.enabled(Feature.saferExceptions) then
702-
disallowRootCapabilitiesIn(tp,
703-
"Result of `try`", "have type",
704-
"This is often caused by a locally generated exception capability leaking as part of its result.",
705-
tree.srcPos)
706-
tp
707686

708687
/* Currently not needed, since capture checking takes place after ElimByName.
709688
* Keep around in case we need to get back to it

compiler/src/dotty/tools/dotc/cc/Setup.scala

-18
Original file line numberDiff line numberDiff line change
@@ -356,29 +356,11 @@ extends tpd.TreeTraverser:
356356
mapRoots
357357
)
358358
capt.println(i"mapped $tree = ${tpt.knownType}")
359-
if allowUniversalInBoxed && tree.symbol.is(Mutable)
360-
&& !tree.symbol.hasAnnotation(defn.UncheckedCapturesAnnot)
361-
then
362-
CheckCaptures.disallowRootCapabilitiesIn(tpt.knownType,
363-
i"Mutable variable ${tree.symbol.name}", "have type",
364-
"This restriction serves to prevent local capabilities from escaping the scope where they are defined.",
365-
tree.srcPos)
366359
traverse(tree.rhs)
367360
case tree @ TypeApply(fn, args) =>
368361
traverse(fn)
369362
for case arg: TypeTree <- args do
370363
transformTT(arg, boxed = true, exact = false, mapRoots = true) // type arguments in type applications are boxed
371-
372-
if allowUniversalInBoxed then
373-
val polyType = atPhase(preRecheckPhase):
374-
fn.tpe.widen.asInstanceOf[TypeLambda]
375-
for case (arg: TypeTree, pinfo, pname) <- args.lazyZip(polyType.paramInfos).lazyZip((polyType.paramNames)) do
376-
if pinfo.bounds.hi.hasAnnotation(defn.Caps_SealedAnnot) then
377-
def where = if fn.symbol.exists then i" in an argument of ${fn.symbol}" else ""
378-
CheckCaptures.disallowRootCapabilitiesIn(arg.knownType,
379-
i"Sealed type variable $pname", "be instantiated to",
380-
i"This is often caused by a local capability$where\nleaking as part of its result.",
381-
tree.srcPos)
382364
case tree: Template =>
383365
inContext(ctx.withOwner(tree.symbol.owner)):
384366
traverseChildren(tree)

compiler/src/dotty/tools/dotc/core/Definitions.scala

-2
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,6 @@ class Definitions {
977977
@tu lazy val Caps_unsafeBox: Symbol = CapsUnsafeModule.requiredMethod("unsafeBox")
978978
@tu lazy val Caps_unsafeUnbox: Symbol = CapsUnsafeModule.requiredMethod("unsafeUnbox")
979979
@tu lazy val Caps_unsafeBoxFunArg: Symbol = CapsUnsafeModule.requiredMethod("unsafeBoxFunArg")
980-
@tu lazy val Caps_SealedAnnot: ClassSymbol = requiredClass("scala.caps.Sealed")
981980

982981
@tu lazy val PureClass: Symbol = requiredClass("scala.Pure")
983982

@@ -1028,7 +1027,6 @@ class Definitions {
10281027
@tu lazy val UncheckedAnnot: ClassSymbol = requiredClass("scala.unchecked")
10291028
@tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable")
10301029
@tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance")
1031-
@tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures")
10321030
@tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile")
10331031
@tu lazy val WithPureFunsAnnot: ClassSymbol = requiredClass("scala.annotation.internal.WithPureFuns")
10341032
@tu lazy val CaptureCheckedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.CaptureChecked")

compiler/src/dotty/tools/dotc/core/Types.scala

+1-16
Original file line numberDiff line numberDiff line change
@@ -4082,15 +4082,10 @@ object Types {
40824082

40834083
protected def toPInfo(tp: Type)(using Context): PInfo
40844084

4085-
/** If `tparam` is a sealed type parameter symbol of a polymorphic method, add
4086-
* a @caps.Sealed annotation to the upperbound in `tp`.
4087-
*/
4088-
protected def addSealed(tparam: ParamInfo, tp: Type)(using Context): Type = tp
4089-
40904085
def fromParams[PI <: ParamInfo.Of[N]](params: List[PI], resultType: Type)(using Context): Type =
40914086
if (params.isEmpty) resultType
40924087
else apply(params.map(_.paramName))(
4093-
tl => params.map(param => toPInfo(addSealed(param, tl.integrate(params, param.paramInfo)))),
4088+
tl => params.map(param => toPInfo(tl.integrate(params, param.paramInfo))),
40944089
tl => tl.integrate(params, resultType))
40954090
}
40964091

@@ -4412,16 +4407,6 @@ object Types {
44124407
resultTypeExp: PolyType => Type)(using Context): PolyType =
44134408
unique(new PolyType(paramNames)(paramInfosExp, resultTypeExp))
44144409

4415-
override protected def addSealed(tparam: ParamInfo, tp: Type)(using Context): Type =
4416-
tparam match
4417-
case tparam: Symbol if tparam.is(Sealed) =>
4418-
tp match
4419-
case tp @ TypeBounds(lo, hi) =>
4420-
tp.derivedTypeBounds(lo,
4421-
AnnotatedType(hi, Annotation(defn.Caps_SealedAnnot, tparam.span)))
4422-
case _ => tp
4423-
case _ => tp
4424-
44254410
def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] =
44264411
Some((tl.typeParams, tl.resType))
44274412
}

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+15-18
Original file line numberDiff line numberDiff line change
@@ -3179,9 +3179,7 @@ object Parsers {
31793179
* id [HkTypeParamClause] TypeParamBounds
31803180
*
31813181
* DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’
3182-
* DefTypeParam ::= {Annotation}
3183-
* [`sealed`] -- under captureChecking
3184-
* id [HkTypeParamClause] TypeParamBounds
3182+
* DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds
31853183
*
31863184
* TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’
31873185
* TypTypeParam ::= {Annotation} id [HkTypePamClause] TypeBounds
@@ -3191,25 +3189,24 @@ object Parsers {
31913189
*/
31923190
def typeParamClause(ownerKind: ParamOwner): List[TypeDef] = inBrackets {
31933191

3194-
def checkVarianceOK(): Boolean =
3195-
val ok = ownerKind != ParamOwner.Def && ownerKind != ParamOwner.TypeParam
3196-
if !ok then syntaxError(em"no `+/-` variance annotation allowed here")
3197-
in.nextToken()
3198-
ok
3192+
def variance(vflag: FlagSet): FlagSet =
3193+
if ownerKind == ParamOwner.Def || ownerKind == ParamOwner.TypeParam then
3194+
syntaxError(em"no `+/-` variance annotation allowed here")
3195+
in.nextToken()
3196+
EmptyFlags
3197+
else
3198+
in.nextToken()
3199+
vflag
31993200

32003201
def typeParam(): TypeDef = {
32013202
val isAbstractOwner = ownerKind == ParamOwner.Type || ownerKind == ParamOwner.TypeParam
32023203
val start = in.offset
3203-
var mods = annotsAsMods() | Param
3204-
if ownerKind == ParamOwner.Class then mods |= PrivateLocal
3205-
if Feature.ccEnabled && in.token == SEALED then
3206-
if ownerKind == ParamOwner.Def then mods |= Sealed
3207-
else syntaxError(em"`sealed` modifier only allowed for method type parameters")
3208-
in.nextToken()
3209-
if isIdent(nme.raw.PLUS) && checkVarianceOK() then
3210-
mods |= Covariant
3211-
else if isIdent(nme.raw.MINUS) && checkVarianceOK() then
3212-
mods |= Contravariant
3204+
val mods =
3205+
annotsAsMods()
3206+
| (if (ownerKind == ParamOwner.Class) Param | PrivateLocal else Param)
3207+
| (if isIdent(nme.raw.PLUS) then variance(Covariant)
3208+
else if isIdent(nme.raw.MINUS) then variance(Contravariant)
3209+
else EmptyFlags)
32133210
atSpan(start, nameStart) {
32143211
val name =
32153212
if (isAbstractOwner && in.token == USCORE) {

compiler/src/dotty/tools/dotc/typer/Checking.scala

+1-6
Original file line numberDiff line numberDiff line change
@@ -517,12 +517,7 @@ object Checking {
517517
// note: this is not covered by the next test since terms can be abstract (which is a dual-mode flag)
518518
// but they can never be one of ClassOnlyFlags
519519
if !sym.isClass && sym.isOneOf(ClassOnlyFlags) then
520-
val illegal = sym.flags & ClassOnlyFlags
521-
if sym.is(TypeParam) && illegal == Sealed && Feature.ccEnabled && cc.allowUniversalInBoxed then
522-
if !sym.owner.is(Method) then
523-
fail(em"only method type parameters can be sealed")
524-
else
525-
fail(em"only classes can be ${illegal.flagsString}")
520+
fail(em"only classes can be ${(sym.flags & ClassOnlyFlags).flagsString}")
526521
if (sym.is(AbsOverride) && !sym.owner.is(Trait))
527522
fail(AbstractOverrideOnlyInTraits(sym))
528523
if sym.is(Trait) then

library/src/scala/caps.scala

-6
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,3 @@ import annotation.experimental
4444
def unsafeBoxFunArg: T => U = f
4545

4646
end unsafe
47-
48-
/** An annotation that expresses the sealed modifier on a type parameter
49-
* Should not be directly referred to in source
50-
*/
51-
@deprecated("The Sealed annotation should not be directly used in source code.\nUse the `sealed` modifier on type parameters instead.")
52-
class Sealed extends annotation.Annotation

tests/neg-custom-args/captures/heal-tparam-cs.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import language.experimental.captureChecking
22

33
trait Cap { def use(): Unit }
44

5-
def localCap[sealed T](op: (c: Cap^{cap}) => T): T = ???
5+
def localCap[T](op: (lcap: caps.Root) ?-> (c: Cap^{lcap}) => T): T = ???
66

77
def main(io: Cap^{cap}, net: Cap^{cap}): Unit = {
88

@@ -11,7 +11,7 @@ def main(io: Cap^{cap}, net: Cap^{cap}): Unit = {
1111
}
1212

1313
val test2: (c: Cap^{cap}) -> () ->{cap} Unit =
14-
localCap { c => // should work
14+
localCap { c => // error, was: should work
1515
(c1: Cap^{cap}) => () => { c1.use() }
1616
}
1717

@@ -25,7 +25,7 @@ def main(io: Cap^{cap}, net: Cap^{cap}): Unit = {
2525
(c1: Cap^{io}) => () => { c1.use() }
2626
}
2727

28-
def localCap2[sealed T](op: (c: Cap^{io}) => T): T = ???
28+
def localCap2[T](op: (c: Cap^{io}) => T): T = ???
2929

3030
val test5: () ->{io} Unit =
3131
localCap2 { c => // ok
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/usingLogFile-alt.scala:21:24 -----------------------------
2-
21 | usingLogger(file)(l => () => l.log("test")) // error
3-
| ^^^^^^^^^^^^^^^^^^^^^^^^
4-
| Found: (l: Test.Logger{val f: java.io.OutputStream^?}^{file}) ->? box () ->? Unit
5-
| Required: (x$0: Test.Logger^{file}) ->{'cap[..<root>](from instantiating usingLogger)} box () ->? Unit
1+
-- Error: tests/neg-custom-args/captures/usingLogFile-alt.scala:18:2 ---------------------------------------------------
2+
18 | usingFile( // error
3+
| ^^^^^^^^^
4+
| reference (file : java.io.OutputStream^{lcap}) is not included in the allowed capture set {x$0, x$0²}
65
|
7-
| Note that reference (cap[Logger] : caps.Root), defined at level 1
8-
| cannot be included in outer capture set ?, defined at level 0 in package <root>
6+
| Note that reference (file : java.io.OutputStream^{lcap}), defined at level 4
7+
| cannot be included in outer capture set {x$0, x$0}, defined at level 0 in package <root>
98
|
10-
| longer explanation available when compiling with `-explain`
9+
| where: x$0 is a reference to a value parameter
10+
| x$0² is a reference to a value parameter

tests/neg-custom-args/captures/usingLogFile-alt.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ object Test:
77
class Logger(f: OutputStream^):
88
def log(msg: String): Unit = ???
99

10-
def usingFile[sealed T](name: String, op: OutputStream^ => T): T =
10+
def usingFile[T](name: String, op: (lcap: caps.Root) ?-> OutputStream^{lcap} => T): T =
1111
val f = new FileOutputStream(name)
1212
val result = op(f)
1313
f.close()
1414
result
1515

16-
def usingLogger[sealed T](f: OutputStream^)(op: Logger^{f} => T): T = ???
16+
def usingLogger[T](f: OutputStream^)(op: Logger^{f} => T): T = ???
1717

18-
usingFile(
18+
usingFile( // error
1919
"foo",
2020
file => {
21-
usingLogger(file)(l => () => l.log("test")) // error
21+
usingLogger(file)(l => () => l.log("test"))
2222
}
2323
)

tests/pos-custom-args/captures/i15749a.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// TODO: Adapt to levels
12
class Unit
23
object u extends Unit
34

@@ -10,12 +11,12 @@ def test =
1011
def wrapper[T](x: T): Wrapper[T] =
1112
[X] => (op: T ->{cap} X) => op(x)
1213

13-
def strictMap[A <: Top, sealed B <: Top](mx: Wrapper[A])(f: A ->{cap} B): Wrapper[B] =
14+
def strictMap[A <: Top, B <: Top](mx: Wrapper[A])(f: A ->{cap} B): Wrapper[B] =
1415
mx((x: A) => wrapper(f(x)))
1516

1617
def force[A](thunk: Unit ->{cap} A): A = thunk(u)
1718

18-
def forceWrapper[sealed A](mx: Wrapper[Unit ->{cap} A]): Wrapper[A] =
19+
def forceWrapper[A](mx: Wrapper[Unit ->{cap} A]): Wrapper[A] =
1920
// Γ ⊢ mx: Wrapper[□ {cap} Unit => A]
2021
// `force` should be typed as ∀(□ {cap} Unit -> A) A, but it can not
2122
strictMap[Unit ->{cap} A, A](mx)(t => force[A](t)) // error

tests/pos-custom-args/captures/vars1.scala

+1-8
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
11
import caps.unsafe.*
2-
import annotation.unchecked.uncheckedCaptures
32

43
object Test:
54
type ErrorHandler = (Int, String) => Unit
65

7-
@uncheckedCaptures
86
var defaultIncompleteHandler: ErrorHandler = ???
9-
@uncheckedCaptures
107
var incompleteHandler: ErrorHandler = defaultIncompleteHandler
118
private val x = incompleteHandler.unsafeUnbox
129
val _ : ErrorHandler = x
1310
val _ = x(1, "a")
1411

15-
def defaultIncompleteHandler1(): (Int, String) => Unit = ???
12+
def defaultIncompleteHandler1(): ErrorHandler = ???
1613
val defaultIncompleteHandler2: ErrorHandler = ???
17-
@uncheckedCaptures
1814
var incompleteHandler1: ErrorHandler = defaultIncompleteHandler1()
19-
@uncheckedCaptures
2015
var incompleteHandler2: ErrorHandler = defaultIncompleteHandler2
21-
@uncheckedCaptures
2216
private var incompleteHandler7 = defaultIncompleteHandler1()
23-
@uncheckedCaptures
2417
private var incompleteHandler8 = defaultIncompleteHandler2
2518

2619
incompleteHandler1 = defaultIncompleteHandler2

tests/pos-special/stdlib/Test1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import java.io.*
66

77
object Test0:
88

9-
def usingLogFile[sealed T](op: FileOutputStream^ => T): T =
9+
def usingLogFile[T](op: (lcap: caps.Root) ?-> FileOutputStream^ => T): T =
1010
val logFile = FileOutputStream("log")
1111
val result = op(logFile)
1212
logFile.close()

tests/pos-with-compiler-cc/dotc/Run.scala

+2-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import scala.collection.mutable
3232
import scala.util.control.NonFatal
3333
import scala.io.Codec
3434
import annotation.constructorOnly
35-
import annotation.unchecked.uncheckedCaptures
35+
import caps.unsafe.unsafeUnbox
3636

3737
/** A compiler run. Exports various methods to compile source files */
3838
class Run(comp: Compiler, @constructorOnly ictx0: Context) extends ImplicitRunInfo with ConstraintRunInfo {
@@ -165,7 +165,6 @@ class Run(comp: Compiler, @constructorOnly ictx0: Context) extends ImplicitRunIn
165165
val staticRefs = util.EqHashMap[Name, Denotation](initialCapacity = 1024)
166166

167167
/** Actions that need to be performed at the end of the current compilation run */
168-
@uncheckedCaptures
169168
private var finalizeActions = mutable.ListBuffer[() => Unit]()
170169

171170
/** Will be set to true if any of the compiled compilation units contains
@@ -276,7 +275,7 @@ class Run(comp: Compiler, @constructorOnly ictx0: Context) extends ImplicitRunIn
276275
Rewrites.writeBack()
277276
suppressions.runFinished(hasErrors = ctx.reporter.hasErrors)
278277
while (finalizeActions.nonEmpty) {
279-
val action = finalizeActions.remove(0)
278+
val action = finalizeActions.remove(0).unsafeUnbox
280279
action()
281280
}
282281
compiling = false

tests/pos-with-compiler-cc/dotc/core/Scopes.scala

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
/* NSC -- new Scala compiler
2+
* Copyright 2005-2012 LAMP/EPFL
3+
* @author Martin Odersky
4+
*/
5+
16
package dotty.tools
27
package dotc
38
package core
@@ -12,7 +17,6 @@ import Denotations._
1217
import printing.Texts._
1318
import printing.Printer
1419
import SymDenotations.NoDenotation
15-
import annotation.unchecked.uncheckedCaptures
1620

1721
import collection.mutable
1822

@@ -216,7 +220,6 @@ object Scopes {
216220
private var elemsCache: List[Symbol] | Null = null
217221

218222
/** The synthesizer to be used, or `null` if no synthesis is done on this scope */
219-
@uncheckedCaptures
220223
private var synthesize: SymbolSynthesizer | Null = null
221224

222225
/** Use specified synthesize for this scope */

0 commit comments

Comments
 (0)