Skip to content

Commit 97f045a

Browse files
committed
First stab at useless checking
Several TODOs need follow-up
1 parent b0e25eb commit 97f045a

Some content is hidden

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

56 files changed

+328
-230
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] =
@@ -571,18 +579,30 @@ extension (sym: Symbol)
571579
def hasTrackedParts(using Context): Boolean =
572580
!CaptureSet.ofTypeDeeply(sym.info).isAlwaysEmpty
573581

574-
/** `sym` itself or its info is annotated @use or it is a type parameter with a matching
575-
* @use-annotated term parameter that contains `sym` in its deep capture set.
582+
/** Until 3.7:
583+
* `sym` itself or its info is annotated @use or it is a type parameter with a matching
584+
* @use-annotated term parameter that contains `sym` in its deep capture set.
585+
* From 3.8:
586+
* `sym` is a capset parameter without a `@reserve` annotation that
587+
* - belongs to a class in a class, or
588+
* - belongs to a method where it appears in a the deep capture set of a following term parameter of the same method.
576589
*/
577590
def isUseParam(using Context): Boolean =
578591
sym.hasAnnotation(defn.UseAnnot)
579592
|| sym.info.hasAnnotation(defn.UseAnnot)
580593
|| sym.is(TypeParam)
581-
&& sym.owner.rawParamss.nestedExists: param =>
582-
param.is(TermParam) && param.hasAnnotation(defn.UseAnnot)
583-
&& param.info.deepCaptureSet.elems.exists:
584-
case c: TypeRef => c.symbol == sym
585-
case _ => false
594+
&& !sym.info.hasAnnotation(defn.ReserveAnnot)
595+
&& (sym.owner.isClass
596+
|| sym.owner.rawParamss.nestedExists: param =>
597+
param.is(TermParam)
598+
&& (!ccConfig.allowUse || param.hasAnnotation(defn.UseAnnot))
599+
&& param.info.deepCaptureSet.elems.exists:
600+
case c: TypeRef => c.symbol == sym
601+
case _ => false
602+
|| {
603+
//println(i"not is use param $sym")
604+
false
605+
})
586606

587607
/** `sym` or its info is annotated with `@consume`. */
588608
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
@@ -1802,6 +1802,7 @@ class CheckCaptures extends Recheck, SymTransformer:
18021802

18031803
checkAnnot(defn.UseAnnot)
18041804
checkAnnot(defn.ConsumeAnnot)
1805+
checkAnnot(defn.ReserveAnnot)
18051806
end OverridingPairsCheckerCC
18061807

18071808
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
@@ -752,7 +752,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
752752
end postProcess
753753

754754
/** Check that @use and @consume annotations only appear on parameters and not on
755-
* anonymous function parameters
755+
* anonymous function parameters. Check that @use annotations don't appear
756+
* at all from 3.8 on.
756757
*/
757758
def checkProperUseOrConsume(tree: Tree)(using Context): Unit = tree match
758759
case tree: MemberDef =>
@@ -766,11 +767,22 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
766767
&& !(sym.is(Method) && sym.owner.isClass)
767768
then
768769
report.error(
769-
em"""@consume cannot be used here. Only memeber methods and their term parameters
770+
em"""@consume cannot be used here. Only member methods and their term parameters
770771
|can have @consume annotations.""",
771772
tree.srcPos)
772773
else if annotCls == defn.UseAnnot then
773-
if !isMethodParam then
774+
if !ccConfig.allowUse then
775+
if sym.is(TypeParam) then
776+
report.error(
777+
em"""@use is redundant here and should no longer be written explicitly.
778+
|Capset variables are always implicitly used, unless they are annotated with @caps.preserve.""",
779+
tree.srcPos)
780+
else
781+
report.error(
782+
em"""@use is no longer supported. Instead of @use you can introduce capset
783+
|variables for the polymorphic parts of parameter types.""",
784+
tree.srcPos)
785+
else if !isMethodParam then
774786
report.error(
775787
em"@use cannot be used here. Only method parameters can have @use annotations.",
776788
tree.srcPos)

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

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

53-
/** If true, turn on separation checking */
54-
def useSepChecks(using Context): Boolean =
55-
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.8`)
56-
5753
/** Not used currently. Handy for trying out new features */
5854
def newScheme(using ctx: Context): Boolean =
5955
Feature.sourceVersion.stable.isAtLeast(SourceVersion.`3.7`)
6056

57+
def allowUse(using Context): Boolean =
58+
Feature.sourceVersion.stable.isAtMost(SourceVersion.`3.7`)
59+
6160
end ccConfig

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

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

14821483
def patch2(denot: ClassDenotation, patchCls: Symbol): Unit =
@@ -2111,8 +2112,9 @@ class Definitions {
21112112
CapsModule, CapsModule.moduleClass, PureClass,
21122113
Caps_Capability, // TODO: Remove when Capability is stabilized
21132114
RequiresCapabilityAnnot,
2114-
captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass, UseAnnot,
2115-
Caps_Mutable, Caps_Sharable, Caps_Control, Caps_Classifier, ConsumeAnnot,
2115+
captureRoot, Caps_CapSet, Caps_ContainsTrait, Caps_ContainsModule, Caps_ContainsModule.moduleClass,
2116+
Caps_Mutable, Caps_Sharable, Caps_Control, Caps_Classifier,
2117+
ConsumeAnnot, UseAnnot, ReserveAnnot,
21162118
CapsUnsafeModule, CapsUnsafeModule.moduleClass,
21172119
CapsInternalModule, CapsInternalModule.moduleClass,
21182120
RetainsAnnot, RetainsCapAnnot, RetainsByNameAnnot)

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

library/src/scala/caps/package.scala

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

73-
/** An annotation on parameters `x` stating that the method's body makes
74-
* use of the reach capability `x*`. Consequently, when calling the method
75-
* we need to charge the deep capture set of the actual argiment to the
73+
/** An annotation on capset parameters `C` stating that the method's body does not
74+
* have `C` in its use-set. `C` might be accessed under a box in the method
75+
* or in the result type of the method. Consequently, when calling the method
76+
* we do not need to charge the capture set of the actual argiment to the
7677
* environment.
7778
*
7879
* Note: This should go into annotations. For now it is here, so that we
7980
* can experiment with it quickly between minor releases
8081
*/
8182
@experimental
83+
final class reserve extends annotation.StaticAnnotation
84+
85+
/** Allowed only for source versions up to 3.7:
86+
* An annotation on parameters `x` stating that the method's body makes
87+
* use of the reach capability `x*`. Consequently, when calling the method
88+
* we need to charge the deep capture set of the actual argiment to the
89+
* environment.
90+
*/
91+
@experimental
8292
final class use extends annotation.StaticAnnotation
8393

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

0 commit comments

Comments
 (0)