Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Three changes to typing rules #14392

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
98bc26d
Merge pull request #12971 from dotty-staging/add-rechecker
odersky Aug 17, 2021
4b88b5a
Cleanups
odersky Aug 17, 2021
5e4e4f0
First version of capture checker.
odersky Sep 29, 2021
2941b6a
Include capture sets of methods in enclosing class
odersky Oct 2, 2021
20cf766
Update iterators example
odersky Nov 19, 2021
f18d951
Add capture checks for mutable variables
odersky Oct 5, 2021
a5d9d06
Implement deep check for variables
odersky Oct 5, 2021
0b130e3
Introduce @capability annotations
odersky Dec 12, 2021
1513393
Fix typo and update semanticdb.expect
odersky Dec 12, 2021
81512d5
Disable MiMa checks
odersky Dec 12, 2021
12f17a4
Special rule for {this} in capture sets of class members
odersky Dec 23, 2021
f134adb
Pure function types -> and ?->
odersky Dec 25, 2021
9bc09ee
Updates for latest master
odersky Jan 21, 2022
e349acc
Shorthand for curried functions
odersky Dec 23, 2021
670e510
Syntax change: allow capture sets in infix types
odersky Jan 26, 2022
7ba7c89
Test for contravariantly used class fields
odersky Jan 26, 2022
42c9eed
Handle captures in by-name parameters
odersky Jan 23, 2022
91299b2
Treat exceptions as capabilities
odersky Jan 25, 2022
9033e0c
Mark classes compiled under -Ycc with a CaptureChecked annotation
odersky Jan 26, 2022
6d672b1
Map regular function types to impure function types when unpickling
odersky Jan 26, 2022
18dd570
New scheme to reject root captures
odersky Jan 28, 2022
4cc8ff0
Three changes to typing rules
odersky Jan 30, 2022
fedd8f2
Change result type of by-name closures
odersky Feb 1, 2022
0ddbbe8
Capture-check exception capabilties
odersky Feb 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ jobs:
./project/scripts/sbt ";dist/pack; scala3-bootstrapped/compile; scala3-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test;sjsCompilerTests/test ;sbt-test/scripted scala2-compat/* ;configureIDE ;stdlib-bootstrapped/test:run ;stdlib-bootstrapped-tasty-tests/test; scala3-compiler-bootstrapped/scala3CompilerCoursierTest:test"
./project/scripts/bootstrapCmdTests

- name: MiMa
run: |
./project/scripts/sbt ";scala3-interfaces/mimaReportBinaryIssues ;scala3-library-bootstrapped/mimaReportBinaryIssues ;scala3-library-bootstrappedJS/mimaReportBinaryIssues; tasty-core-bootstrapped/mimaReportBinaryIssues"
# - name: MiMa
# run: |
# ./project/scripts/sbt ";scala3-interfaces/mimaReportBinaryIssues ;scala3-library-bootstrapped/mimaReportBinaryIssues ;scala3-library-bootstrappedJS/mimaReportBinaryIssues; tasty-core-bootstrapped/mimaReportBinaryIssues"

test_windows_fast:
runs-on: [self-hosted, Windows]
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package dotc
import core._
import Contexts._
import typer.{TyperPhase, RefChecks}
import cc.CheckCaptures
import parsing.Parser
import Phases.Phase
import transform._
Expand Down Expand Up @@ -81,6 +82,8 @@ class Compiler {
new SpecializeApplyMethods, // Adds specialized methods to FunctionN
new TryCatchPatterns, // Compile cases in try/catch
new PatternMatcher) :: // Compile pattern matches
List(new PreRecheck) :: // Preparations for check captures phase, enabled under -Ycc
List(new CheckCaptures) :: // Check captures, enabled under -Ycc
List(new ElimOpaque, // Turn opaque into normal aliases
new sjs.ExplicitJSClasses, // Make all JS classes explicit (Scala.js only)
new ExplicitOuter, // Add accessors to outer classes from nested ones.
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import reporting.{Reporter, Suppression, Action}
import reporting.Diagnostic
import reporting.Diagnostic.Warning
import rewrites.Rewrites

import profile.Profiler
import printing.XprintMode
import typer.ImplicitRunInfo
Expand Down Expand Up @@ -320,7 +319,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
val fusedPhase = ctx.base.fusedContaining(prevPhase)
val echoHeader = f"[[syntax trees at end of $fusedPhase%25s]] // ${unit.source}"
val tree = if ctx.isAfterTyper then unit.tpdTree else unit.untpdTree
val treeString = tree.show(using ctx.withProperty(XprintMode, Some(())))
val treeString = fusedPhase.show(tree)

last match {
case SomePrintedTree(phase, lastTreeString) if lastTreeString == treeString =>
Expand Down
15 changes: 12 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ object desugar {

if mods.is(Trait) then
for vparams <- originalVparamss; vparam <- vparams do
if vparam.tpt.isInstanceOf[ByNameTypeTree] then
if isByNameType(vparam.tpt) then
report.error(em"implementation restriction: traits cannot have by name parameters", vparam.srcPos)

// Annotations on class _type_ parameters are set on the derived parameters
Expand Down Expand Up @@ -558,9 +558,8 @@ object desugar {
appliedTypeTree(tycon, targs)
}

def isRepeated(tree: Tree): Boolean = tree match {
def isRepeated(tree: Tree): Boolean = stripByNameType(tree) match {
case PostfixOp(_, Ident(tpnme.raw.STAR)) => true
case ByNameTypeTree(tree1) => isRepeated(tree1)
case _ => false
}

Expand Down Expand Up @@ -1733,6 +1732,14 @@ object desugar {
flatTree(pats1 map (makePatDef(tree, mods, _, rhs)))
case ext: ExtMethods =>
Block(List(ext), Literal(Constant(())).withSpan(ext.span))
case CapturingTypeTree(refs, parent) =>
def annotate(annotName: TypeName, tp: Tree) =
Annotated(tp, New(scalaDot(annotName), List(refs)))
parent match
case ByNameTypeTree(restpt) =>
cpy.ByNameTypeTree(parent)(annotate(tpnme.retainsByName, restpt))
case _ =>
annotate(tpnme.retains, parent)
}
desugared.withSpan(tree.span)
}
Expand Down Expand Up @@ -1871,6 +1878,8 @@ object desugar {
case _ => traverseChildren(tree)
}
}.traverse(expr)
case CapturingTypeTree(refs, parent) =>
collect(parent)
case _ =>
}
collect(tree)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/MainProxies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package ast
import core._
import Symbols._, Types._, Contexts._, Decorators._, util.Spans._, Flags._, Constants._
import StdNames.nme
import ast.Trees._
import Trees._

/** Generate proxy classes for @main functions.
* A function like
Expand Down
23 changes: 21 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
}

/** Is tpt a vararg type of the form T* or => T*? */
def isRepeatedParamType(tpt: Tree)(using Context): Boolean = tpt match {
case ByNameTypeTree(tpt1) => isRepeatedParamType(tpt1)
def isRepeatedParamType(tpt: Tree)(using Context): Boolean = stripByNameType(tpt) match {
case tpt: TypeTree => tpt.typeOpt.isRepeatedParam
case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true
case _ => false
Expand All @@ -190,6 +189,16 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case arg => arg.typeOpt.widen.isRepeatedParam
}

def isByNameType(tree: Tree)(using Context): Boolean =
stripByNameType(tree) ne tree

def stripByNameType(tree: Tree)(using Context): Tree = unsplice(tree) match
case ByNameTypeTree(t1) => t1
case untpd.CapturingTypeTree(_, parent) =>
val parent1 = stripByNameType(parent)
if parent1 eq parent then tree else parent1
case _ => tree

/** All type and value parameter symbols of this DefDef */
def allParamSyms(ddef: DefDef)(using Context): List[Symbol] =
ddef.paramss.flatten.map(_.symbol)
Expand Down Expand Up @@ -389,6 +398,16 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
case _ => None
}
}

object ImpureByNameTypeTree:
def apply(tp: ByNameTypeTree)(using Context): untpd.CapturingTypeTree =
untpd.CapturingTypeTree(
Ident(nme.CAPTURE_ROOT).withSpan(tp.span.startPos) :: Nil, tp)
def unapply(tp: Tree)(using Context): Option[ByNameTypeTree] = tp match
case untpd.CapturingTypeTree(id @ Ident(nme.CAPTURE_ROOT) :: Nil, bntp: ByNameTypeTree)
if id.span == bntp.span.startPos => Some(bntp)
case _ => None
end ImpureByNameTypeTree
}

trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
Expand Down
8 changes: 1 addition & 7 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -253,16 +253,10 @@ object Trees {
/** Tree's denotation can be derived from its type */
abstract class DenotingTree[-T >: Untyped](implicit @constructorOnly src: SourceFile) extends Tree[T] {
type ThisTree[-T >: Untyped] <: DenotingTree[T]
override def denot(using Context): Denotation = typeOpt match {
override def denot(using Context): Denotation = typeOpt.stripped match
case tpe: NamedType => tpe.denot
case tpe: ThisType => tpe.cls.denot
case tpe: AnnotatedType => tpe.stripAnnots match {
case tpe: NamedType => tpe.denot
case tpe: ThisType => tpe.cls.denot
case _ => NoDenotation
}
case _ => NoDenotation
}
}

/** Tree's denot/isType/isTerm properties come from a subtree
Expand Down
17 changes: 15 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class InterpolatedString(id: TermName, segments: List[Tree])(implicit @constructorOnly src: SourceFile)
extends TermTree

/** A function type */
/** A function type or closure */
case class Function(args: List[Tree], body: Tree)(implicit @constructorOnly src: SourceFile) extends Tree {
override def isTerm: Boolean = body.isTerm
override def isType: Boolean = body.isType
}

/** A function type with `implicit`, `erased`, or `given` modifiers */
/** A function type or closure with `implicit`, `erased`, or `given` modifiers */
class FunctionWithMods(args: List[Tree], body: Tree, val mods: Modifiers)(implicit @constructorOnly src: SourceFile)
extends Function(args, body)

Expand Down Expand Up @@ -147,6 +147,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case Floating
}

/** {x1, ..., xN} T (only relevant under -Ycc) */
case class CapturingTypeTree(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree

/** Short-lived usage in typer, does not need copy/transform/fold infrastructure */
case class DependentTypeTree(tp: List[Symbol] => Type)(implicit @constructorOnly src: SourceFile) extends Tree

Expand Down Expand Up @@ -214,6 +217,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class Transparent()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Transparent)

case class Infix()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Infix)

case class Impure()(implicit @constructorOnly src: SourceFile) extends Mod(Flags.Impure)
}

/** Modifiers and annotations for definitions
Expand Down Expand Up @@ -650,6 +655,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case tree: Number if (digits == tree.digits) && (kind == tree.kind) => tree
case _ => finalize(tree, untpd.Number(digits, kind))
}
def CapturingTypeTree(tree: Tree)(refs: List[Tree], parent: Tree)(using Context): Tree = tree match
case tree: CapturingTypeTree if (refs eq tree.refs) && (parent eq tree.parent) => tree
case _ => finalize(tree, untpd.CapturingTypeTree(refs, parent))

def TypedSplice(tree: Tree)(splice: tpd.Tree)(using Context): ProxyTree = tree match {
case tree: TypedSplice if splice `eq` tree.splice => tree
case _ => finalize(tree, untpd.TypedSplice(splice)(using ctx))
Expand Down Expand Up @@ -715,6 +724,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
tree
case MacroTree(expr) =>
cpy.MacroTree(tree)(transform(expr))
case CapturingTypeTree(refs, parent) =>
cpy.CapturingTypeTree(tree)(transform(refs), transform(parent))
case _ =>
super.transformMoreCases(tree)
}
Expand Down Expand Up @@ -776,6 +787,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
this(x, splice)
case MacroTree(expr) =>
this(x, expr)
case CapturingTypeTree(refs, parent) =>
this(this(x, refs), parent)
case _ =>
super.foldMoreCases(x, tree)
}
Expand Down
65 changes: 65 additions & 0 deletions compiler/src/dotty/tools/dotc/cc/CaptureAnnotation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package dotty.tools
package dotc
package cc

import core.*
import Types.*, Symbols.*, Contexts.*, Annotations.*
import ast.Trees.*
import ast.{tpd, untpd}
import Decorators.*
import config.Printers.capt
import printing.Printer
import printing.Texts.Text


case class CaptureAnnotation(refs: CaptureSet, kind: CapturingKind) extends Annotation:
import CaptureAnnotation.*
import tpd.*

override def tree(using Context) =
val elems = refs.elems.toList.map {
case cr: TermRef => ref(cr)
case cr: TermParamRef => untpd.Ident(cr.paramName).withType(cr)
case cr: ThisType => This(cr.cls)
}
val arg = repeated(elems, TypeTree(defn.AnyType))
New(symbol.typeRef, arg :: Nil)

override def symbol(using Context) =
if kind == CapturingKind.ByName then defn.RetainsByNameAnnot else defn.RetainsAnnot

override def derivedAnnotation(tree: Tree)(using Context): Annotation =
unsupported("derivedAnnotation(Tree)")

def derivedAnnotation(refs: CaptureSet, kind: CapturingKind)(using Context): Annotation =
if (this.refs eq refs) && (this.kind == kind) then this
else CaptureAnnotation(refs, kind)

override def sameAnnotation(that: Annotation)(using Context): Boolean = that match
case CaptureAnnotation(refs2, kind2) => refs == refs2 && kind == kind2
case _ => false

override def mapWith(tp: TypeMap)(using Context) =
val elems = refs.elems.toList
val elems1 = elems.mapConserve(tp)
if elems1 eq elems then this
else if elems1.forall(_.isInstanceOf[CaptureRef])
then derivedAnnotation(CaptureSet(elems1.asInstanceOf[List[CaptureRef]]*), kind)
else EmptyAnnotation

override def refersToParamOf(tl: TermLambda)(using Context): Boolean =
refs.elems.exists {
case TermParamRef(tl1, _) => tl eq tl1
case _ => false
}

override def toText(printer: Printer): Text = refs.toText(printer)

override def hash: Int =
(refs.hashCode << 1) | (if kind == CapturingKind.Regular then 0 else 1)

override def eql(that: Annotation) = that match
case that: CaptureAnnotation => (this.refs eq that.refs) && (this.kind == kind)
case _ => false

end CaptureAnnotation
Loading