Skip to content

Commit c409c5d

Browse files
committed
First stab at useless checking
Several TODOs need follow-up
1 parent 27a7ce7 commit c409c5d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+292
-190
lines changed

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

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,17 @@ extension (tp: Type)
9999
case AnnotatedType(tp1, ann) if tp1.derivesFrom(defn.Caps_CapSet) && ann.symbol.isRetains =>
100100
ann.tree.retainedSet.retainedElementsRaw
101101
case tp =>
102-
// Nothing is a special type to represent the empty set
103-
if tp.isNothingType then Nil
104-
else tp :: Nil // should be checked by wellformedness
102+
tp.dealiasKeepAnnots match
103+
case tp: TypeRef if tp.symbol == defn.Caps_CapSet =>
104+
// This can happen in cases where we try to type an eta expansion `$x => f($x)`
105+
// from a polymorphic target type using capture sets. In that case the parameter type
106+
// of $x is not treated as inferred is approximated to CapSet. An example is
107+
// capset-problem.scala. We handle these cases by appromxating to the empty set.
108+
Nil
109+
case _ =>
110+
// Nothing is a special type to represent the empty set
111+
if tp.isNothingType then Nil
112+
else tp :: Nil // should be checked by wellformedness
105113

106114
/** A list of capabilities of a retained set. */
107115
def retainedElements(using Context): List[Capability] =
@@ -575,18 +583,30 @@ extension (sym: Symbol)
575583
def hasTrackedParts(using Context): Boolean =
576584
!CaptureSet.ofTypeDeeply(sym.info).isAlwaysEmpty
577585

578-
/** `sym` itself or its info is annotated @use or it is a type parameter with a matching
579-
* @use-annotated term parameter that contains `sym` in its deep capture set.
586+
/** Until 3.7:
587+
* `sym` itself or its info is annotated @use or it is a type parameter with a matching
588+
* @use-annotated term parameter that contains `sym` in its deep capture set.
589+
* From 3.8:
590+
* `sym` is a capset parameter without a `@reserve` annotation that
591+
* - belongs to a class in a class, or
592+
* - belongs to a method where it appears in a the deep capture set of a following term parameter of the same method.
580593
*/
581594
def isUseParam(using Context): Boolean =
582595
sym.hasAnnotation(defn.UseAnnot)
583596
|| sym.info.hasAnnotation(defn.UseAnnot)
584597
|| sym.is(TypeParam)
585-
&& sym.owner.rawParamss.nestedExists: param =>
586-
param.is(TermParam) && param.hasAnnotation(defn.UseAnnot)
587-
&& param.info.deepCaptureSet.elems.exists:
588-
case c: TypeRef => c.symbol == sym
589-
case _ => false
598+
&& !sym.info.hasAnnotation(defn.ReserveAnnot)
599+
&& (sym.owner.isClass
600+
|| sym.owner.rawParamss.nestedExists: param =>
601+
param.is(TermParam)
602+
&& (!ccConfig.allowUse || param.hasAnnotation(defn.UseAnnot))
603+
&& param.info.deepCaptureSet.elems.exists:
604+
case c: TypeRef => c.symbol == sym
605+
case _ => false
606+
|| {
607+
//println(i"not is use param $sym")
608+
false
609+
})
590610

591611
/** `sym` or its info is annotated with `@consume`. */
592612
def isConsumeParam(using Context): Boolean =

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,6 +1765,7 @@ class CheckCaptures extends Recheck, SymTransformer:
17651765

17661766
checkAnnot(defn.UseAnnot)
17671767
checkAnnot(defn.ConsumeAnnot)
1768+
checkAnnot(defn.ReserveAnnot)
17681769
end OverridingPairsCheckerCC
17691770

17701771
def traverse(t: Tree)(using Context) =

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
742742
end postProcess
743743

744744
/** Check that @use and @consume annotations only appear on parameters and not on
745-
* anonymous function parameters
745+
* anonymous function parameters. Check that @use annotations don't appear
746+
* at all from 3.8 on.
746747
*/
747748
def checkProperUseOrConsume(tree: Tree)(using Context): Unit = tree match
748749
case tree: MemberDef =>
@@ -756,11 +757,22 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
756757
&& !(sym.is(Method) && sym.owner.isClass)
757758
then
758759
report.error(
759-
em"""@consume cannot be used here. Only memeber methods and their term parameters
760+
em"""@consume cannot be used here. Only member methods and their term parameters
760761
|can have @consume annotations.""",
761762
tree.srcPos)
762763
else if annotCls == defn.UseAnnot then
763-
if !isMethodParam then
764+
if !ccConfig.allowUse then
765+
if sym.is(TypeParam) then
766+
report.error(
767+
em"""@use is redundant here and should no longer be written explicitly.
768+
|Capset variables are always implicitly used, unless they are annotated with @caps.preserve.""",
769+
tree.srcPos)
770+
else
771+
report.error(
772+
em"""@use is no longer supported. Instead of @use you can introduce capset
773+
|variables for the polymorphic parts of parameter types.""",
774+
tree.srcPos)
775+
else if !isMethodParam then
764776
report.error(
765777
em"@use cannot be used here. Only method parameters can have @use annotations.",
766778
tree.srcPos)

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,11 @@ object ccConfig:
4747
def useFreshLevels(using Context): Boolean =
4848
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`)
4949

50-
/** If true, turn on separation checking */
51-
def useSepChecks(using Context): Boolean =
52-
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.8`)
53-
5450
/** Not used currently. Handy for trying out new features */
5551
def newScheme(using ctx: Context): Boolean =
5652
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`)
5753

54+
def allowUse(using Context): Boolean =
55+
Feature.sourceVersion.stable.isAtMost(SourceVersion.`3.7`)
56+
5857
end ccConfig

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,8 +1075,9 @@ class Definitions {
10751075
@tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance")
10761076
@tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures")
10771077
@tu lazy val UntrackedCapturesAnnot: ClassSymbol = requiredClass("scala.caps.unsafe.untrackedCaptures")
1078-
@tu lazy val UseAnnot: ClassSymbol = requiredClass("scala.caps.use")
1079-
@tu lazy val ConsumeAnnot: ClassSymbol = requiredClass("scala.caps.consume")
1078+
@tu lazy val UseAnnot: ClassSymbol = requiredClass("scala.caps.use")
1079+
@tu lazy val ConsumeAnnot: ClassSymbol = requiredClass("scala.caps.consume")
1080+
@tu lazy val ReserveAnnot: ClassSymbol = requiredClass("scala.caps.reserve")
10801081
@tu lazy val RefineOverrideAnnot: ClassSymbol = requiredClass("scala.caps.internal.refineOverride")
10811082
@tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile")
10821083
@tu lazy val LanguageFeatureMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.languageFeature")
@@ -1478,7 +1479,7 @@ class Definitions {
14781479
def patchStdLibClass(denot: ClassDenotation)(using Context): Unit =
14791480
// Do not patch the stdlib files if we explicitly disable it
14801481
// This is only to be used during the migration of the stdlib
1481-
if ctx.settings.YnoStdlibPatches.value then
1482+
if ctx.settings.YnoStdlibPatches.value then
14821483
return
14831484

14841485
def patch2(denot: ClassDenotation, patchCls: Symbol): Unit =
@@ -2114,8 +2115,10 @@ class Definitions {
21142115
Caps_Capability, // TODO: Remove when Capability is stabilized
21152116
Caps_Classifier, Caps_SharedCapability, Caps_Control, Caps_ExclusiveCapability, Caps_Mutable, Caps_Read,
21162117
RequiresCapabilityAnnot,
2117-
captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass, UseAnnot,
2118-
ConsumeAnnot, CapsUnsafeModule, CapsUnsafeModule.moduleClass,
2118+
captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass,
2119+
Caps_Mutable, Caps_Sharable, Caps_Control, Caps_Classifier,
2120+
ConsumeAnnot, UseAnnot, ReserveAnnot,
2121+
CapsUnsafeModule, CapsUnsafeModule.moduleClass,
21192122
CapsInternalModule, CapsInternalModule.moduleClass,
21202123
RetainsAnnot, RetainsCapAnnot, RetainsByNameAnnot)
21212124

compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
137137
keywordText("erased ").provided(isErased)
138138
~ specialAnnotText(defn.UseAnnot, arg)
139139
~ specialAnnotText(defn.ConsumeAnnot, arg)
140+
~ specialAnnotText(defn.ReserveAnnot, arg)
140141
~ homogenizeArg(arg).match
141142
case arg: TypeBounds => "?" ~ toText(arg)
142143
case arg => toText(arg)

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
602602
def typedIdent(tree: untpd.Ident, pt: Type)(using Context): Tree =
603603
record("typedIdent")
604604
val name = tree.name
605-
def kind = if (name.isTermName) "" else "type "
605+
def kind =
606+
if name.isTermName then
607+
if ctx.mode.is(Mode.InCaptureSet) then "capability "
608+
else ""
609+
else "type "
606610
typr.println(s"typed ident $kind$name in ${ctx.owner}")
607611
if ctx.mode.is(Mode.Pattern) then
608612
if name == nme.WILDCARD then

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class CompilationTests {
3737
compileFilesInDir("tests/pos-special/sourcepath/outer", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")),
3838
compileFile("tests/pos-special/sourcepath/outer/nested/Test4.scala", defaultOptions.and("-sourcepath", "tests/pos-special/sourcepath")),
3939
compileFilesInDir("tests/pos-scala2", defaultOptions.and("-source", "3.0-migration")),
40-
compileFilesInDir("tests/pos-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking", "-language:experimental.separationChecking")),
40+
compileFilesInDir("tests/pos-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking", "-language:experimental.separationChecking", "-source", "3.8")),
4141
compileFile("tests/pos-special/utf8encoded.scala", defaultOptions.and("-encoding", "UTF8")),
4242
compileFile("tests/pos-special/utf16encoded.scala", defaultOptions.and("-encoding", "UTF16")),
4343
compileDir("tests/pos-special/i18589", defaultOptions.and("-Wsafe-init").without("-Ycheck:all")),
@@ -149,7 +149,7 @@ class CompilationTests {
149149
aggregateTests(
150150
compileFilesInDir("tests/neg", defaultOptions, FileFilter.exclude(TestSources.negScala2LibraryTastyExcludelisted)),
151151
compileFilesInDir("tests/neg-deep-subtype", allowDeepSubtypes),
152-
compileFilesInDir("tests/neg-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking", "-language:experimental.separationChecking")),
152+
compileFilesInDir("tests/neg-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking", "-language:experimental.separationChecking", "-source", "3.8")),
153153
compileFile("tests/neg-custom-args/sourcepath/outer/nested/Test1.scala", defaultOptions.and("-sourcepath", "tests/neg-custom-args/sourcepath")),
154154
compileDir("tests/neg-custom-args/sourcepath2/hi", defaultOptions.and("-sourcepath", "tests/neg-custom-args/sourcepath2", "-Xfatal-warnings")),
155155
compileList("duplicate source", List(
@@ -172,7 +172,7 @@ class CompilationTests {
172172
aggregateTests(
173173
compileFilesInDir("tests/run", defaultOptions.and("-Wsafe-init")),
174174
compileFilesInDir("tests/run-deep-subtype", allowDeepSubtypes),
175-
compileFilesInDir("tests/run-custom-args/captures", allowDeepSubtypes.and("-language:experimental.captureChecking", "-language:experimental.separationChecking")),
175+
compileFilesInDir("tests/run-custom-args/captures", allowDeepSubtypes.and("-language:experimental.captureChecking", "-language:experimental.separationChecking", "-source", "3.8")),
176176
// Run tests for legacy lazy vals.
177177
compileFilesInDir("tests/run", defaultOptions.and("-Wsafe-init", "-Ylegacy-lazy-vals", "-Ycheck-constraint-deps"), FileFilter.include(TestSources.runLazyValsAllowlist)),
178178
).checkRuns()

library/src/scala/caps/package.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,25 @@ object Contains:
8787
@experimental
8888
given containsImpl[C >: CapSet <: CapSet @retainsCap, R <: Singleton]: Contains[C, R]()
8989

90-
/** An annotation on parameters `x` stating that the method's body makes
91-
* use of the reach capability `x*`. Consequently, when calling the method
92-
* we need to charge the deep capture set of the actual argiment to the
90+
/** An annotation on capset parameters `C` stating that the method's body does not
91+
* have `C` in its use-set. `C` might be accessed under a box in the method
92+
* or in the result type of the method. Consequently, when calling the method
93+
* we do not need to charge the capture set of the actual argiment to the
9394
* environment.
9495
*
9596
* Note: This should go into annotations. For now it is here, so that we
9697
* can experiment with it quickly between minor releases
9798
*/
9899
@experimental
100+
final class reserve extends annotation.StaticAnnotation
101+
102+
/** Allowed only for source versions up to 3.7:
103+
* An annotation on parameters `x` stating that the method's body makes
104+
* use of the reach capability `x*`. Consequently, when calling the method
105+
* we need to charge the deep capture set of the actual argiment to the
106+
* environment.
107+
*/
108+
@experimental
99109
final class use extends annotation.StaticAnnotation
100110

101111
/** An annotations on parameters and update methods.

project/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2320,7 +2320,7 @@ object Build {
23202320
settings(scala2LibraryBootstrappedSettings).
23212321
settings(
23222322
moduleName := "scala2-library-cc",
2323-
scalacOptions += "-language:experimental.separationChecking" // for separation checking
2323+
scalacOptions ++= List("-language:experimental.separationChecking", "-source", "3.8") // for @use changes
23242324
)
23252325

23262326
lazy val scala2LibraryBootstrappedSettings = Seq(

0 commit comments

Comments
 (0)